2012-01-20 19 views
11

Tengo un modelo django y un campo que representa el nombre completo de un usuario. Mi cliente quiere que configure un filtro para buscar un usuario basado en una matriz de cadenas donde todas ellas deben ser insensibles a las mayúsculas y a las minúsculas contenidas dentro del nombre completo.Filtrar la base de datos Django para el campo que contiene cualquier valor en una matriz

Por ejemplo

Si un usuario full_name = "Keith, Thomson S."

y tengo una lista ['keith','s','thomson']

que quieren llevar a cabo el filtro equivalente de

Profile.objects.filter(full_name__icontains='keith',full_name__icontains='s',full_name__icontains='thomson') 

El problema es que esta lista puede ser de tamaño dinámico, así que no sé cómo hacer esto.

¿Alguien tiene alguna idea?

Respuesta

33

Realizar llamadas sucesivas a filter, así:

queryset = Profile.objects.all() 
strings = ['keith', 's', 'thompson'] 
for string in strings: 
    queryset = queryset.filter(full_name__icontains=string) 

alternativa, se puede & juntos un montón de Q objetos:

condition = Q(full_name__icontains=s[0]) 
for string in strings[1:]: 
    condition &= Q(full_name__icontains=string) 
queryset = Profile.objects.filter(condition) 

Una forma más críptico de escribir esto, evitando el bucle explícito :

import operator 
# ... 
condition = reduce(operator.and_, [Q(full_name__icontains=s) for s in strings]) 
queryset = Profile.objects.filter(condition) 
+0

Me pregunto cómo será la expresión sql final después de encadenar todos estos filtros. – akonsu

+0

@akonsu Acabo de probarlo (bueno, algo similar) - se traduce a una serie de 'AND's en la cláusula' where', es decir 'full_name LIKE% keith% AND full_name LIKE% s% AND ... ' –

+2

@isbadawi, +1 - tenga en cuenta que el operador predeterminado para múltiples objetos Q es AND, por lo que puede simplemente' * [Q1, Q2, Q3] 'sin usar reduce/operator.and_. –

2

somethi ng a lo largo de estas líneas:


array = ['keith', 's', 'thomson'] 
regex = '^.*(%s).*$' % '|'.join(array) 
Profile.objects.filter(full_name__iregex=regex) 

EDIT: esto es incorrecto, el PO quiere nombres que contienen todas las cadenas de forma simultánea.

3

Incluso más breve utilizando los operator funciones and_ o or_ combinar la lista de Q() condiciones

from operator import and_, or_ 
li = ['keith', 's', 'thompson'] 

elementos que coinciden con todas las cuerdas (and_)

Profile.objects.filter(reduce(and_, [Q(full_name__icontains=q) for q in li])) 

Los elementos que coincidan con cualquiera de las cuerdas (or_)

Profile.objects.filter(reduce(or_, [Q(full_name__icontains=q) for q in li])) 

La función Q() implementa __or__() y __and__() para unir dos objetos Q() juntos, por lo que puede ser llamado usando las correspondientes funciones operator.

Cuestiones relacionadas