Django filtre queryset __pour *chaque* dans la liste
Disons que j'ai les modèles suivants
class Photo(models.Model):
tags = models.ManyToManyField(Tag)
class Tag(models.Model):
name = models.CharField(max_length=50)
En vue, j'ai une liste avec les filtres actifs appelé catégories.
Je veux filtre Photo des objets qui ont tous les tags présents dans catégories.
J'ai essayé:
Photo.objects.filter(tags__name__in=categories)
Mais cela correspond tout élément dans les catégories, pas tous éléments.
Donc, si les catégories seraient ['vacances', 'été'] je veux une Photo avec à la fois des vacances et l'été de tag.
Cela peut-il être atteint?
- Peut-être: qs=Photo.objets.tous les(); pour la catégorie dans les catégories: qs = qs.filtre(balises__nom=catégorie)
- jpic est droit,
Photo.objects.filter(tags__name='holiday').filter(tags__name='summer')
est le chemin à parcourir. (Ce sont les mêmes que jpic de l'exemple). Chaquefilter
devrait ajouter plus deJOIN
s de requête, de sorte que vous pouvez prendre annotation approche si ils sont trop nombreux. - Voici la référence dans la documentation: docs.djangoproject.com/en/dev/topics/db/queries/...
Vous devez vous connecter pour publier un commentaire.
Résumé:
Une option est, comme l'a suggéré jpic et sgallen dans les commentaires, d'ajouter
.filter()
pour chaque catégorie. Chaquefilter
ajoute plus de jointures, ce qui ne devrait pas être un problème pour petit ensemble de catégories.Il est le l'agrégation approche. Cette requête devrait être plus court et peut-être plus rapide pour un grand nombre de catégories.
Vous avez également la possibilité d'utiliser requêtes personnalisées.
Quelques exemples
Configuration de Test:
À l'aide de les filtres chaînés approche:
Requête:
Noter que chaque
filter
ajoute plus deJOINS
à la requête.À l'aide de annotation approche:
Requête:
AND
edQ
objets ne fonctionnerait pas:Requête:
t3
, et une photo a l'tagst2
ett3
. Alors cette photo sera toujours correspondre à la requête donnée.Photo.objects.filter(tags__in=tags)
correspond à des photos qui ont l'une quelconque des balises, pas seulement de celles qui a toutes les. Certains de ceux qui n'en a qu'un, de la de tags, peuvent avoir exactement la quantité de balises que vous êtes à la recherche pour, et certains de ceux qui a toutes les balises, peuvent aussi avoir d'autres balises.Q
les objets de travail, maisOR
-ed à la place ?Une autre approche qui fonctionne, bien que PostgreSQL seulement, est à l'aide de
django.contrib.postgres.fields.ArrayField
:Exemple copié à partir de docs:
ArrayField
il a plus de fonctionnalités puissantes telles que chevauchement et index transforme.Cela peut aussi être fait par la dynamique de génération de requête à l'aide de l'ORM de Django et Python magique 🙂
L'idée est de générer des objets Q pour chaque catégorie et de les combiner à l'aide ET à l'exploitant dans un QuerySet. E. g. pour votre exemple, il serait égal à
filter
serait le même que l'utilisation deand
de Q objets dans un filtre... Mon erreur.J'utilise une petite fonction qui effectue une itération de filtres sur une liste pour un opérateur donné un nom de colonne :
et cette fonction peut être appelée comme ça:
il fonctionne aussi avec n'importe quelle classe et plus de balises dans la liste; les opérateurs peuvent être n'importe qui, comme "iexact','en','contient','ne',...
Si nous voulons le faire de manière dynamique, a suivi l'exemple: