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)
Dans une vue, j'ai une liste avec des filtres actifs appelés catégories . Je souhaite filtrer les objets photo dont toutes les balises sont présentes dans des catégories .
J'ai essayé:
Photo.objects.filter(tags__name__in=categories)
Mais cela correspond à n'importe quel élément des catégories, pas à tous les éléments.
Donc, si les catégories sont ['vacances', 'été'], je veux des photos avec à la fois une balise vacances et été.
Cela peut-il être réalisé?
python
django
filter
django-queryset
Sander van Leeuwen
la source
la source
Photo.objects.filter(tags__name='holiday').filter(tags__name='summer')
c'est la voie à suivre. (C'est le même que l'exemple de jpic). Chacunfilter
devrait ajouter plus deJOIN
s à la requête, vous pouvez donc adopter une approche d'annotation s'ils sont trop nombreux.Réponses:
Résumé:
Une option est, comme suggéré par jpic et sgallen dans les commentaires, d'ajouter
.filter()
pour chaque catégorie. Chaque ajoutfilter
ajoute plus de jointures, ce qui ne devrait pas poser de problème pour un petit ensemble de catégories.Il y a l' agrégation approche d' . Cette requête serait plus courte et peut-être plus rapide pour un grand ensemble de catégories.
Vous avez également la possibilité d'utiliser des requêtes personnalisées .
Quelques exemples
Configuration du test:
Utilisation de filtres chaînés approche des :
Requête résultante:
Notez que chaque
filter
ajoute plusJOINS
à la requête.Utilisation de l' approche d' annotation :
Requête résultante:
AND
LesQ
objets ed ne fonctionneraient pas:Requête résultante:
la source
t3
, et une photo a les balisest2
ett3
. Ensuite, cette photo correspondra toujours à la requête donnée.Photo.objects.filter(tags__in=tags)
correspond aux photos qui ont l'une des balises, pas seulement celles qui ont tout. Certains de ceux qui n'ont qu'une seule des balises souhaitées peuvent avoir exactement le nombre de balises que vous recherchez, et certains de ceux qui ont toutes les balises souhaitées peuvent également avoir des balises supplémentaires.Une autre approche qui fonctionne, bien que PostgreSQL ™ uniquement, consiste à utiliser
django.contrib.postgres.fields.ArrayField
:Exemple copié à partir de documents :
ArrayField
possède des fonctionnalités plus puissantes telles que le chevauchement et les transformations d'index .la source
Cela peut également être fait par la génération de requêtes dynamiques à l'aide de Django ORM et de la magie Python :)
L'idée est de générer des objets Q appropriés pour chaque catégorie, puis de les combiner à l'aide de l'opérateur AND en un seul QuerySet. Par exemple, pour votre exemple, ce serait égal à
la source
filter
serait la même chose que l'utilisationand
d'objets Q dans un filtre ... Mon erreur.filter
àexclude
et utilisez un opérateur de négation. Comme ça:res = Photo.exclude(~reduce(and_, [Q(tags__name=c) for c in categories]))
J'utilise une petite fonction qui itère les filtres sur une liste pour un opérateur donné et un nom de colonne:
et cette fonction peut être appelée comme ça:
il fonctionne également avec n'importe quelle classe et plus de balises dans la liste; les opérateurs peuvent être n'importe qui comme 'iexact', 'in', 'contains', 'ne', ...
la source
la source
Si nous voulons le faire de manière dynamique, suivez l'exemple:
la source