Résumé :
Une option est, comme suggéré par jpic et sgallen dans les commentaires, d'ajouter .filter()
pour chaque catégorie. Chaque filter
ajoute plus de jointures, ce qui ne devrait pas être un problème pour un petit ensemble de catégories.
Il y a le agrégation approche . Cette requête serait plus courte et peut-être plus rapide pour un grand nombre de catégories.
Vous avez également la possibilité d'utiliser les requêtes personnalisées .
Quelques exemples
Configuration du test :
class Photo(models.Model):
tags = models.ManyToManyField('Tag')
class Tag(models.Model):
name = models.CharField(max_length=50)
def __unicode__(self):
return self.name
In [2]: t1 = Tag.objects.create(name='holiday')
In [3]: t2 = Tag.objects.create(name='summer')
In [4]: p = Photo.objects.create()
In [5]: p.tags.add(t1)
In [6]: p.tags.add(t2)
In [7]: p.tags.all()
Out[7]: [<Tag: holiday>, <Tag: summer>]
In [8]: Photo.objects.filter(tags=t1).filter(tags=t2)
Out[8]: [<Photo: Photo object>]
Requête résultante :
In [17]: print Photo.objects.filter(tags=t1).filter(tags=t2).query
SELECT "test_photo"."id"
FROM "test_photo"
INNER JOIN "test_photo_tags" ON ("test_photo"."id" = "test_photo_tags"."photo_id")
INNER JOIN "test_photo_tags" T4 ON ("test_photo"."id" = T4."photo_id")
WHERE ("test_photo_tags"."tag_id" = 3 AND T4."tag_id" = 4 )
Notez que chaque filter
ajoute plus JOINS
à la requête.
In [29]: from django.db.models import Count
In [30]: Photo.objects.filter(tags__in=[t1, t2]).annotate(num_tags=Count('tags')).filter(num_tags=2)
Out[30]: [<Photo: Photo object>]
Requête résultante :
In [32]: print Photo.objects.filter(tags__in=[t1, t2]).annotate(num_tags=Count('tags')).filter(num_tags=2).query
SELECT "test_photo"."id", COUNT("test_photo_tags"."tag_id") AS "num_tags"
FROM "test_photo"
LEFT OUTER JOIN "test_photo_tags" ON ("test_photo"."id" = "test_photo_tags"."photo_id")
WHERE ("test_photo_tags"."tag_id" IN (3, 4))
GROUP BY "test_photo"."id", "test_photo"."id"
HAVING COUNT("test_photo_tags"."tag_id") = 2
AND
ed Q
ne fonctionnerait pas :
In [9]: from django.db.models import Q
In [10]: Photo.objects.filter(Q(tags__name='holiday') & Q(tags__name='summer'))
Out[10]: []
In [11]: from operator import and_
In [12]: Photo.objects.filter(reduce(and_, [Q(tags__name='holiday'), Q(tags__name='summer')]))
Out[12]: []
Requête résultante :
In [25]: print Photo.objects.filter(Q(tags__name='holiday') & Q(tags__name='summer')).query
SELECT "test_photo"."id"
FROM "test_photo"
INNER JOIN "test_photo_tags" ON ("test_photo"."id" = "test_photo_tags"."photo_id")
INNER JOIN "test_tag" ON ("test_photo_tags"."tag_id" = "test_tag"."id")
WHERE ("test_tag"."name" = holiday AND "test_tag"."name" = summer )
7 votes
Maybe : qs=Photo.objects.all() ; for category in categories : qs = qs.filter(tags__name=category)
2 votes
Jpic a raison,
Photo.objects.filter(tags__name='holiday').filter(tags__name='summer')
est la voie à suivre. (C'est la même chose que l'exemple de jpic). Chaquefilter
devrait ajouter plusJOIN
à interroger, donc vous pourriez prendre approche d'annotation s'ils sont trop nombreux.1 votes
Voici la référence dans les docs : docs.djangoproject.com/fr/dev/topics/db/queries/
1 votes
On s'attendrait à ce qu'il y ait une fonction intégrée pour cela dans Django.