2012-08-10 18 views
13

digamos que tengo un modelo de dirección con un campo de código postal. Me puede buscar direcciones con el código postal que comienza con "123" con esta línea:Django startswith en los campos

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

Ahora, tengo que hacer esta búsqueda del "revés". Tengo un modelo de Dirección con un campo de prefijo de código postal, y necesito recuperar todas las direcciones para las cuales el prefijo de código postal es un prefijo de un código dado, como "12345". Entonces, si en mi base de datos tengo 2 direcciones con postcode_prefix = "123" y "234", solo se devolverá la primera.

Algo así como:

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

El problema es que esto no funciona. La única solución que puedo llegar a es realizar un filtro en el primer carácter, como:

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

y luego, cuando llegue a los resultados, hacer una lista por comprensión de que las filtra adecuadamente, así:

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

¿Hay una mejor manera de hacerlo en django? gracias, Fabricio

+0

¿La longitud del prefijo está fija o tiene longitud variable? – cyroxx

+0

longitud variable :) – sfabriz

Respuesta

8

En términos de SQL, lo que quiere lograr lee como ('12345' es el código postal que está buscando):

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

esto no es realmente una consulta estándar y no veo ninguna posibilidad de lograr esto en Django usando sólo get()/filtro().

Sin embargo, Django ofrece una manera de proporcionar cláusulas adicionales con SQL extra():

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

Por favor ver la Django documentation on extra() para mayor referencia. También tenga en cuenta que el extra contiene SQL puro, por lo que debe asegurarse de que la cláusula sea válida para su base de datos.

Espero que esto funcione para usted.

+1

Gracias. Esta es probablemente la mejor manera de hacerlo, sin embargo, como dices, se vuelve dependiente de db. Estoy feliz de que no haya una magia rápida de django que me faltaba. ¡Muchas gracias! Fabrizio – sfabriz

11

Creo que lo que estamos tratando de hacer con su "algo así como" línea está escrito correctamente como esto:

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

En realidad, OP lo quiere al revés. – cyroxx

+0

Exactamente. Necesito al revés. Puedo hacerlo con Python, en más de una forma, pero solo necesitaba saber si me faltaba alguna forma adecuada de django para hacerlo. :) – sfabriz

0

A. Si no es el tema https://code.djangoproject.com/ticket/13363, que podría hacer esto:

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

Tal vez, se va a arreglar un problema y que puede funcionar.

B. Si no es el problema 16731 ​​(no se proporciona la URL completa, no hay suficiente reputación, vea otro ticket más arriba) puede filtrar por campos que se agregaron con '.annotate', con creación de función de aggreación personalizada, como aquí: http://coder.cl/2011/09/custom-aggregates-on-django/

C. Último y exitoso. He conseguido hacer esto utilizando monkeypatching de los siguientes:

  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

de búsqueda a medida solo se define '_ aperturas', que tiene inversa lógica de ' _startswith'

0

Una posible alternativa. (No tienen idea de cómo se compara con la solución aceptada con una columna como segundo parámetro a recibir, en tiempo de ejecución)

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

De este modo, se genera todos los prefijos, y juntos ... o

Cuestiones relacionadas