El filtro Django consulta en “tuplas” de valores para varias columnas

Digamos que tengo un modelo:

Class Person(models.Model): firstname = models.CharField() lastname = models.CharField() birthday = models.DateField() # etc... 

y digo que tengo una lista de 2 nombres: first_list = ['Bob', 'Rob'] Y tengo una lista de 2 apellidos: last_list = ['Williams', 'Williamson'] . Luego, si quisiera seleccionar a todos aquellos cuyo primer nombre estuviera en first_list podría ejecutar:

 Person.objects.filter(firstname__in=first_list) 

y si quisiera seleccionar a todos aquellos cuyo apellido estuviera en last_list , podría hacer:

 Person.objects.filter(lastname__in=last_list) 

Hasta ahora tan bueno. Si quiero ejecutar ambas restricciones al mismo tiempo, eso es fácil …

 Person.objects.filter(firstname__in=first_list, lastname__in=last_list) 

Si quisiera hacer la búsqueda de estilo or lugar de la búsqueda de estilo and , puedo hacerlo con objetos Q :

 Person.objects.filter(Q(firstname__in=first_list) | Q(lastname__in=last_name)) 

Pero lo que tengo en mente es algo un poco más sutil. ¿Qué pasa si solo quiero devolver un queryset que devuelve combinaciones específicas de nombres y apellidos? Es decir, quiero devolver los objetos Person para los cuales (Person.firstname, Person.lastname) está en zip(first_names, last_names) . Es decir, quiero recuperar a alguien llamado Bob Williams o Rob Williamson (pero no a nadie llamado Bob Williamson o Rob Williams).

En mi caso de uso real, first_list y last_list tendrían ~ 100 elementos.

En este momento, necesito resolver este problema en una aplicación Django. Pero también tengo curiosidad acerca de la mejor manera de manejar esto en un contexto de SQL más general.

¡Gracias! (Y, por favor, avíseme si puedo aclarar algo).

No veo muchas soluciones a excepción de una gran cláusula OR:

 import operator from itertools import izip query = reduce( operator.or_, (Q(firstname=fn, lastname=ln) for fn, ln in izip(first_list, last_list)) ) Person.objects.filter(query) 

La respuesta de bruno funciona, pero me parece sucia, tanto en el nivel de Python como en el nivel de SQL (una gran concatenación de OR). Al menos en MySQL, puedes usar la siguiente syntax SQL:

 SELECT id FROM table WHERE (first_name, last_name) IN (('John','Doe'),('Jane','Smith'),('Bill','Clinton')) 

El ORM de Django no proporciona una forma directa de hacer esto, por lo que utilizo SQL sin formato:

 User.objects.raw('SELECT * FROM table WHERE (first_name, last_name) IN %s', [ (('John','Doe'),('Jane','Smith'),('Bill','Clinton')) ]) 

(Esta es una lista con un elemento, que coincide con el% s en la consulta. El elemento es una iterable de tuplas, por lo que% s se convertirá en una lista SQL de tuplas).

Notas:

  1. Como dije, esto funciona para MySQL. No estoy seguro de qué otros backends soportan esta syntax.
  2. Se solucionó un error en Python-mysql relacionado con este comportamiento en noviembre de 2013 / MySQLdb 1.2.4, así que asegúrese de que las bibliotecas de Python MySQLdb no sean anteriores.