2012-08-10 6 views
13

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

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

Maintenant, je dois faire cette recherche la « inverse ». J'ai un modèle d'adresse avec un champ de préfixe de code postal, et j'ai besoin de récupérer toutes les adresses pour lesquelles préfixe de code postal 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", seul le premier serait retourné.

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 est d'effectuer un filtre sur le premier char, comme:

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

puis, quand je reçois les résultats, faire une compréhension de liste qui les filtre correctement, comme ceci:

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

Existe-t-il une meilleure façon de le faire en django? merci, Fabrizio

+0

La longueur du préfixe est-elle fixe ou a-t-elle une longueur variable? – cyroxx

+0

longueur variable :) – sfabriz

Répondre

8

En termes SQL, ce que vous voulez atteindre 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 seulement obtenir()/filtre().

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

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

S'il vous plaît voir le Django documentation on extra() pour référence ultérieure. Notez également que l'extra contient du SQL pur, donc vous devez vous assurer que la clause est valide pour votre base de données.

Espérons que cela fonctionne pour vous.

+1

Merci. C'est probablement la meilleure façon de le faire, cependant, comme vous le dites, elle devient dépendante de DB. Je suis heureux cependant qu'il n'y ait pas de magie de django rapide qui me manquait. Merci beaucoup! Fabrizio – sfabriz

11

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

Address.objects.filter(postcode__startswith=postcode_prefix) 
+0

En fait, OP veut l'inverse. – cyroxx

+0

Exactement. J'ai besoin de l'inverse. Je peux le faire avec python, à plus d'un titre, mais j'avais juste besoin de savoir s'il me manquait une bonne façon de le faire. :) – sfabriz

0

A. Dans le cas contraire la question https://code.djangoproject.com/ticket/13363, vous pouvez faire ceci:

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

Peut-être, ils vont résoudre un problème et il peut fonctionner. B. Si pas le problème 16731 ​​(désolé de ne pas fournir l'URL complète, pas assez de rep, voir un autre ticket ci-dessus) vous pouvez filtrer par champs ajoutés avec '.annotate', avec création de fonction d'agrégation personnalisée, comme ici: http://coder.cl/2011/09/custom-aggregates-on-django/

C. Dernière et réussie. J'ai réussi à le faire en utilisant 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 '_ commence', qui a inversé logique de ' _startswith'

0

Une alternative possible. (Ne sais pas comment il se compare à la solution retenue avec une colonne en tant que deuxième à aimer param, dans le 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 ensemble ...