web-dev-qa-db-fra.com

Django commence par sur les champs

disons que j'ai un modèle d'adresse avec un champ de code postal. Je peux rechercher des adresses avec un code postal commençant par "123" avec cette ligne:

Address.objects.filter(postcode__startswith="123")

Maintenant, je dois faire cette recherche "dans l'autre sens". J'ai un modèle d'adresse avec un champ postcode_prefix, et je dois récupérer toutes les adresses pour lesquelles postcode_prefix est un préfixe d'un code donné, comme "12345". Donc, si dans ma base de données j'avais 2 adresses avec postcode_prefix = "123" et "234", seule la première serait retournée.

Quelque chose comme:

Address.objects.filter("12345".startswith(postcode_prefix)) 

Le problème est que cela ne fonctionne pas. La seule solution que je peux trouver consiste à effectuer un filtre sur le premier caractère, comme:

Address.objects.filter(postcode_prefix__startswith="12345"[0])

puis, quand j'obtiens les résultats, faites une liste de compréhension qui les filtre correctement, comme ceci:

results = [r for r in results if "12345".startswith(r.postcode_prefix)]

Y a-t-il une meilleure façon de le faire dans Django? merci, Fabrizio

22
sfabriz

En termes SQL, ce que vous voulez obtenir se lit comme ('12345' est le code postal que vous recherchez):

SELECT *
FROM address
WHERE '12345' LIKE postcode_prefix||'%'

Ce n'est pas vraiment une requête standard et je ne vois aucune possibilité d'y parvenir dans Django en utilisant uniquement get ()/filter ().

Cependant, Django offre un moyen de fournir des clauses SQL supplémentaires avec extra():

postcode = '12345'
Address.objects.extra(where=["%s LIKE postcode_prefix||'%%'"], params=[postcode])

Veuillez consulter la documentation Django sur extra () pour plus de référence. Notez également que le supplément contient du SQL pur, vous devez donc vous assurer que la clause est valide pour votre base de données.

J'espère que cela fonctionne pour toi.

9
cyroxx

Je pense que ce que vous essayez de faire avec votre ligne "quelque chose comme" est correctement écrit comme ceci:

Address.objects.filter(postcode__startswith=postcode_prefix)
26
Joe Day

Une bouchée, mais vous pouvez le faire en annotant votre valeur de recherche, puis en la filtrant. Tout se passe assez rapidement dans la base de données.

from Django.db.models import Value as V, F, CharField

MyModel.objects.annotate(
    search_str=Value('...', output_field=CharField())
).filter(
    search_str__istartswith=F('my_previx_field')
)

Je suis sûr que vous pouvez le faire via une fonction de modèle Nice ces jours-ci aussi ... Mais c'est assez propre pour moi.

1
Oli

Une alternative possible. (Je n'ai aucune idée de la façon dont elle se compare à la solution acceptée avec une colonne comme deuxième paramètre à aimer, en temps d'exécution)

q=reduce(lambda a,b:a|b, [Q(postcode__startswith=postcode[:i+1]) for i in range(len(postcode))])

Ainsi, vous générez tous les préfixes, et/ou eux ensemble ...

0
Vajk Hermecz

A. Si ce n'est pas le problème https://code.djangoproject.com/ticket/1336 , vous pouvez le faire:

queryset.extra(select={'myconst': "'this superstring is myconst value'"}).filter(myconst__contains=F('myfield'))

Peut-être, ils vont résoudre un problème et cela peut fonctionner.

B.Si ce n'est pas le problème 16731 ​​(désolé de ne pas fournir d'URL complète, pas assez de représentants, voir un autre ticket ci-dessus), vous pouvez filtrer par les champs ajoutés avec '.annotate', avec la création d'une fonction d'agrégation personnalisée, comme ici: http://coder.cl/2011/09/custom-aggregates-on-Django/

C. Dernier et réussi. J'ai réussi à le faire en utilisant le monkeypatching des éléments suivants:

  1. Django.db.models.sql.Query.query_terms
  2. Django.db.models.fields.Field.get_prep_lookup
  3. Django.db.models.fields.Field.get_db_prep_lookup
  4. Django.db.models.sql.where.WhereNode.make_atom

Recherche personnalisée juste définie "_ démarre", qui a une logique inverse de " _ démarre avec"

0
brawaga