Digamos que tengo los siguientes modelos
class Photo(models.Model):
tags = models.ManyToManyField(Tag)
class Tag(models.Model):
name = models.CharField(max_length=50)
En una vista, tengo una lista con filtros activos llamados categorías . Quiero filtrar los objetos fotográficos que tienen todas las etiquetas presentes en las categorías .
Lo intenté:
Photo.objects.filter(tags__name__in=categories)
Pero esto coincide con cualquier elemento de las categorías, no con todos los elementos.
Entonces, si las categorías fueran ['vacaciones', 'verano'], quiero fotos con etiquetas de vacaciones y verano.
¿Se puede lograr esto?
python
django
filter
django-queryset
Sander van Leeuwen
fuente
fuente
Photo.objects.filter(tags__name='holiday').filter(tags__name='summer')
es el camino a seguir. (Esto es lo mismo que el ejemplo de jpic). Cada unofilter
debe agregar másJOIN
mensajes de correo electrónico a la consulta, por lo que podría adoptar un enfoque de anotación si son demasiados.Respuestas:
Resumen:
Una opción es, como sugirieron jpic y sgallen en los comentarios, agregar
.filter()
para cada categoría. Cada adicionalfilter
agrega más combinaciones, lo que no debería ser un problema para un pequeño conjunto de categorías.Existe el enfoque de agregación . Esta consulta sería más corta y quizás más rápida para un gran conjunto de categorías.
También tiene la opción de utilizar consultas personalizadas .
Algunos ejemplos
Configuración de prueba:
Usando el enfoque de filtros encadenados :
Consulta resultante:
Tenga en cuenta que cada uno
filter
agrega másJOINS
a la consulta.Usando el enfoque de anotación :
Consulta resultante:
AND
LosQ
objetos ed no funcionarían:Consulta resultante:
fuente
t3
, y una foto tiene las etiquetast2
yt3
. Entonces, esta foto seguirá coincidiendo con la consulta dada)Photo.objects.filter(tags__in=tags)
coincide con las fotos que tienen alguna de las etiquetas, no solo las que las tienen todas. Algunos de los que solo tienen una de las etiquetas deseadas, pueden tener exactamente la cantidad de etiquetas que está buscando, y algunos de los que tienen todas las etiquetas deseadas, también pueden tener etiquetas adicionales.Otro enfoque que funciona, aunque solo PostgreSQL, es usar
django.contrib.postgres.fields.ArrayField
:Ejemplo copiado de documentos :
ArrayField
tiene algunas características más poderosas, como la superposición y las transformaciones de índice .fuente
Esto también se puede hacer mediante la generación de consultas dinámicas usando Django ORM y algo de magia de Python :)
La idea es generar objetos Q apropiados para cada categoría y luego combinarlos usando el operador AND en un QuerySet. Por ejemplo, para su ejemplo sería igual a
fuente
filter
sería lo mismo que usarand
para objetos Q en un filtro ... Mi error.filter
aexclude
y utilizar un operador cambiar signo. Así:res = Photo.exclude(~reduce(and_, [Q(tags__name=c) for c in categories]))
Utilizo una pequeña función que itera filtros sobre una lista para un operador dado y un nombre de columna:
y esta función se puede llamar así:
también funciona con cualquier clase y más etiquetas en la lista; los operadores pueden ser cualquiera como 'iexact', 'in', 'contains', 'ne', ...
fuente
fuente
Si queremos hacerlo de forma dinámica, seguimos el ejemplo:
fuente