Django filter queryset __in for *every* item in list












78














Let's say I have the following models



class Photo(models.Model):
tags = models.ManyToManyField(Tag)

class Tag(models.Model):
name = models.CharField(max_length=50)


In a view I have a list with active filters called categories.
I want to filter Photo objects which have all tags present in categories.



I tried:



Photo.objects.filter(tags__name__in=categories)


But this matches any item in categories, not all items.



So if categories would be ['holiday', 'summer'] I want Photo's with both a holiday and summer tag.



Can this be achieved?










share|improve this question




















  • 6




    Maybe: qs=Photo.objects.all(); for category in categories: qs = qs.filter(tags__name=category)
    – jpic
    Dec 23 '11 at 16:07








  • 2




    jpic is right, Photo.objects.filter(tags__name='holiday').filter(tags__name='summer') is the way to go. (This is same as jpic's example). Each filter should add more JOINs to query, so you could take annotation approach if they are too many.
    – Davor Lucic
    Dec 23 '11 at 16:34








  • 1




    Here's the reference in the docs: docs.djangoproject.com/en/dev/topics/db/queries/…
    – sgallen
    Dec 25 '11 at 1:23
















78














Let's say I have the following models



class Photo(models.Model):
tags = models.ManyToManyField(Tag)

class Tag(models.Model):
name = models.CharField(max_length=50)


In a view I have a list with active filters called categories.
I want to filter Photo objects which have all tags present in categories.



I tried:



Photo.objects.filter(tags__name__in=categories)


But this matches any item in categories, not all items.



So if categories would be ['holiday', 'summer'] I want Photo's with both a holiday and summer tag.



Can this be achieved?










share|improve this question




















  • 6




    Maybe: qs=Photo.objects.all(); for category in categories: qs = qs.filter(tags__name=category)
    – jpic
    Dec 23 '11 at 16:07








  • 2




    jpic is right, Photo.objects.filter(tags__name='holiday').filter(tags__name='summer') is the way to go. (This is same as jpic's example). Each filter should add more JOINs to query, so you could take annotation approach if they are too many.
    – Davor Lucic
    Dec 23 '11 at 16:34








  • 1




    Here's the reference in the docs: docs.djangoproject.com/en/dev/topics/db/queries/…
    – sgallen
    Dec 25 '11 at 1:23














78












78








78


28





Let's say I have the following models



class Photo(models.Model):
tags = models.ManyToManyField(Tag)

class Tag(models.Model):
name = models.CharField(max_length=50)


In a view I have a list with active filters called categories.
I want to filter Photo objects which have all tags present in categories.



I tried:



Photo.objects.filter(tags__name__in=categories)


But this matches any item in categories, not all items.



So if categories would be ['holiday', 'summer'] I want Photo's with both a holiday and summer tag.



Can this be achieved?










share|improve this question















Let's say I have the following models



class Photo(models.Model):
tags = models.ManyToManyField(Tag)

class Tag(models.Model):
name = models.CharField(max_length=50)


In a view I have a list with active filters called categories.
I want to filter Photo objects which have all tags present in categories.



I tried:



Photo.objects.filter(tags__name__in=categories)


But this matches any item in categories, not all items.



So if categories would be ['holiday', 'summer'] I want Photo's with both a holiday and summer tag.



Can this be achieved?







python django filter django-queryset






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Feb 23 '12 at 13:25

























asked Dec 23 '11 at 16:01









Sander van Leeuwen

1,56511626




1,56511626








  • 6




    Maybe: qs=Photo.objects.all(); for category in categories: qs = qs.filter(tags__name=category)
    – jpic
    Dec 23 '11 at 16:07








  • 2




    jpic is right, Photo.objects.filter(tags__name='holiday').filter(tags__name='summer') is the way to go. (This is same as jpic's example). Each filter should add more JOINs to query, so you could take annotation approach if they are too many.
    – Davor Lucic
    Dec 23 '11 at 16:34








  • 1




    Here's the reference in the docs: docs.djangoproject.com/en/dev/topics/db/queries/…
    – sgallen
    Dec 25 '11 at 1:23














  • 6




    Maybe: qs=Photo.objects.all(); for category in categories: qs = qs.filter(tags__name=category)
    – jpic
    Dec 23 '11 at 16:07








  • 2




    jpic is right, Photo.objects.filter(tags__name='holiday').filter(tags__name='summer') is the way to go. (This is same as jpic's example). Each filter should add more JOINs to query, so you could take annotation approach if they are too many.
    – Davor Lucic
    Dec 23 '11 at 16:34








  • 1




    Here's the reference in the docs: docs.djangoproject.com/en/dev/topics/db/queries/…
    – sgallen
    Dec 25 '11 at 1:23








6




6




Maybe: qs=Photo.objects.all(); for category in categories: qs = qs.filter(tags__name=category)
– jpic
Dec 23 '11 at 16:07






Maybe: qs=Photo.objects.all(); for category in categories: qs = qs.filter(tags__name=category)
– jpic
Dec 23 '11 at 16:07






2




2




jpic is right, Photo.objects.filter(tags__name='holiday').filter(tags__name='summer') is the way to go. (This is same as jpic's example). Each filter should add more JOINs to query, so you could take annotation approach if they are too many.
– Davor Lucic
Dec 23 '11 at 16:34






jpic is right, Photo.objects.filter(tags__name='holiday').filter(tags__name='summer') is the way to go. (This is same as jpic's example). Each filter should add more JOINs to query, so you could take annotation approach if they are too many.
– Davor Lucic
Dec 23 '11 at 16:34






1




1




Here's the reference in the docs: docs.djangoproject.com/en/dev/topics/db/queries/…
– sgallen
Dec 25 '11 at 1:23




Here's the reference in the docs: docs.djangoproject.com/en/dev/topics/db/queries/…
– sgallen
Dec 25 '11 at 1:23












5 Answers
5






active

oldest

votes


















104














Summary:



One option is, as suggested by jpic and sgallen in the comments, to add .filter() for each category. Each additional filter adds more joins, which should not be a problem for small set of categories.



There is the aggregation approach. This query would be shorter and perhaps quicker for a large set of categories.



You also have the option of using custom queries.





Some examples



Test setup:



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>]


Using chained filters approach:



In [8]: Photo.objects.filter(tags=t1).filter(tags=t2)
Out[8]: [<Photo: Photo object>]


Resulting query:



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 )


Note that each filter adds more JOINS to the query.



Using annotation approach:



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>]


Resulting query:



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



ANDed Q objects would not work:



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]:


Resulting query:



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 )





share|improve this answer



















  • 5




    Is there be a solution with a custom lookup? docs.djangoproject.com/en/1.10/howto/custom-lookups It would cool to switch "__in" to "__all" and have it create the correct sql query.
    – t1m0
    Jan 18 '17 at 16:06






  • 1




    This annotation solution seems wrong. What if there are three tags possible (lets call the additional one for t3, and a photo has the tags t2 and t3. Then this photo will still match the given query.
    – beruic
    Apr 30 at 11:53










  • @beruic I think the idea is that you would replace num_tags=2 with num_tags=len(tags); I expect the hard-coded 2 was just for example's sake.
    – tbm
    May 2 at 17:29






  • 1




    @tbm It still would not work. Photo.objects.filter(tags__in=tags) matches photos that have any of the tags, not only those that has all. Some of those that only has one of the desired tags, may have exactly the amount of tags that you are looking for, and some of those that has all the desired tags, may also have additional tags.
    – beruic
    May 2 at 18:53










  • If your Django version is 1.8 or above you could use conditional annotations. See this answer: stackoverflow.com/a/33777815/1031534
    – beruic
    May 2 at 19:14



















5














Another approach that works, although PostgreSQL only, is using django.contrib.postgres.fields.ArrayField:



Example copied from docs:



>>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
>>> Post.objects.create(name='Second post', tags=['thoughts'])
>>> Post.objects.create(name='Third post', tags=['tutorial', 'django'])

>>> Post.objects.filter(tags__contains=['thoughts'])
<QuerySet [<Post: First post>, <Post: Second post>]>

>>> Post.objects.filter(tags__contains=['django'])
<QuerySet [<Post: First post>, <Post: Third post>]>

>>> Post.objects.filter(tags__contains=['django', 'thoughts'])
<QuerySet [<Post: First post>]>


ArrayField has some more powerful features such as overlap and index transforms.






share|improve this answer





























    2














    This also can be done by dynamic query generation using Django ORM and some Python magic :)



    from operator import and_
    from django.db.models import Q

    categories = ['holiday', 'summer']
    res = Photo.filter(reduce(and_, [Q(tags__name=c) for c in categories]))


    The idea is to generate appropriate Q objects for each category and then combine them using AND operator into one QuerySet. E.g. for your example it'd be equal to



    res = Photo.filter(Q(tags__name='holiday') & Q(tags__name='summer'))





    share|improve this answer

















    • 2




      This would not work. Your query examples would not return anything for the models in question.
      – Davor Lucic
      Dec 26 '11 at 17:30










    • Thanks for correction. I thought chaining filter would be the same as using and for Q objects in one filter... My mistake.
      – demalexx
      Dec 27 '11 at 0:05










    • No worries, my first thought where also Q objects.
      – Davor Lucic
      Dec 27 '11 at 0:48






    • 1




      This would we slower if you work with large tables and large data to compare to. (like 1 Million each)
      – gies0r
      May 21 at 16:11



















    0














    I use a little function that iterates filters over a list for a given operator an a column name :



    def exclusive_in (cls,column,operator,value_list):         
    myfilter = column + '__' + operator
    query = cls.objects
    for value in value_list:
    query=query.filter(**{myfilter:value})
    return query


    and this function can be called like that:



    exclusive_in(Photo,'tags__name','iexact',['holiday','summer'])


    it also work with any class and more tags in the list; operators can be anyone like 'iexact','in','contains','ne',...






    share|improve this answer





























      -1














      If we want to do it dynamically, followed the example:



      tag_ids = [t1.id, t2.id]
      qs = Photo.objects.all()

      for tag_id in tag_ids:
      qs = qs.filter(tag__id=tag_id)

      print qs





      share|improve this answer





















      • Cannot work as as soon as the second iteration, the queryset will be empty
        – lapin
        Jul 31 at 8:45











      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f8618068%2fdjango-filter-queryset-in-for-every-item-in-list%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      5 Answers
      5






      active

      oldest

      votes








      5 Answers
      5






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      104














      Summary:



      One option is, as suggested by jpic and sgallen in the comments, to add .filter() for each category. Each additional filter adds more joins, which should not be a problem for small set of categories.



      There is the aggregation approach. This query would be shorter and perhaps quicker for a large set of categories.



      You also have the option of using custom queries.





      Some examples



      Test setup:



      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>]


      Using chained filters approach:



      In [8]: Photo.objects.filter(tags=t1).filter(tags=t2)
      Out[8]: [<Photo: Photo object>]


      Resulting query:



      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 )


      Note that each filter adds more JOINS to the query.



      Using annotation approach:



      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>]


      Resulting query:



      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



      ANDed Q objects would not work:



      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]:


      Resulting query:



      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 )





      share|improve this answer



















      • 5




        Is there be a solution with a custom lookup? docs.djangoproject.com/en/1.10/howto/custom-lookups It would cool to switch "__in" to "__all" and have it create the correct sql query.
        – t1m0
        Jan 18 '17 at 16:06






      • 1




        This annotation solution seems wrong. What if there are three tags possible (lets call the additional one for t3, and a photo has the tags t2 and t3. Then this photo will still match the given query.
        – beruic
        Apr 30 at 11:53










      • @beruic I think the idea is that you would replace num_tags=2 with num_tags=len(tags); I expect the hard-coded 2 was just for example's sake.
        – tbm
        May 2 at 17:29






      • 1




        @tbm It still would not work. Photo.objects.filter(tags__in=tags) matches photos that have any of the tags, not only those that has all. Some of those that only has one of the desired tags, may have exactly the amount of tags that you are looking for, and some of those that has all the desired tags, may also have additional tags.
        – beruic
        May 2 at 18:53










      • If your Django version is 1.8 or above you could use conditional annotations. See this answer: stackoverflow.com/a/33777815/1031534
        – beruic
        May 2 at 19:14
















      104














      Summary:



      One option is, as suggested by jpic and sgallen in the comments, to add .filter() for each category. Each additional filter adds more joins, which should not be a problem for small set of categories.



      There is the aggregation approach. This query would be shorter and perhaps quicker for a large set of categories.



      You also have the option of using custom queries.





      Some examples



      Test setup:



      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>]


      Using chained filters approach:



      In [8]: Photo.objects.filter(tags=t1).filter(tags=t2)
      Out[8]: [<Photo: Photo object>]


      Resulting query:



      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 )


      Note that each filter adds more JOINS to the query.



      Using annotation approach:



      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>]


      Resulting query:



      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



      ANDed Q objects would not work:



      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]:


      Resulting query:



      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 )





      share|improve this answer



















      • 5




        Is there be a solution with a custom lookup? docs.djangoproject.com/en/1.10/howto/custom-lookups It would cool to switch "__in" to "__all" and have it create the correct sql query.
        – t1m0
        Jan 18 '17 at 16:06






      • 1




        This annotation solution seems wrong. What if there are three tags possible (lets call the additional one for t3, and a photo has the tags t2 and t3. Then this photo will still match the given query.
        – beruic
        Apr 30 at 11:53










      • @beruic I think the idea is that you would replace num_tags=2 with num_tags=len(tags); I expect the hard-coded 2 was just for example's sake.
        – tbm
        May 2 at 17:29






      • 1




        @tbm It still would not work. Photo.objects.filter(tags__in=tags) matches photos that have any of the tags, not only those that has all. Some of those that only has one of the desired tags, may have exactly the amount of tags that you are looking for, and some of those that has all the desired tags, may also have additional tags.
        – beruic
        May 2 at 18:53










      • If your Django version is 1.8 or above you could use conditional annotations. See this answer: stackoverflow.com/a/33777815/1031534
        – beruic
        May 2 at 19:14














      104












      104








      104






      Summary:



      One option is, as suggested by jpic and sgallen in the comments, to add .filter() for each category. Each additional filter adds more joins, which should not be a problem for small set of categories.



      There is the aggregation approach. This query would be shorter and perhaps quicker for a large set of categories.



      You also have the option of using custom queries.





      Some examples



      Test setup:



      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>]


      Using chained filters approach:



      In [8]: Photo.objects.filter(tags=t1).filter(tags=t2)
      Out[8]: [<Photo: Photo object>]


      Resulting query:



      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 )


      Note that each filter adds more JOINS to the query.



      Using annotation approach:



      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>]


      Resulting query:



      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



      ANDed Q objects would not work:



      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]:


      Resulting query:



      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 )





      share|improve this answer














      Summary:



      One option is, as suggested by jpic and sgallen in the comments, to add .filter() for each category. Each additional filter adds more joins, which should not be a problem for small set of categories.



      There is the aggregation approach. This query would be shorter and perhaps quicker for a large set of categories.



      You also have the option of using custom queries.





      Some examples



      Test setup:



      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>]


      Using chained filters approach:



      In [8]: Photo.objects.filter(tags=t1).filter(tags=t2)
      Out[8]: [<Photo: Photo object>]


      Resulting query:



      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 )


      Note that each filter adds more JOINS to the query.



      Using annotation approach:



      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>]


      Resulting query:



      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



      ANDed Q objects would not work:



      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]:


      Resulting query:



      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 )






      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited May 3 '13 at 9:26









      Hedde van der Heide

      14k104379




      14k104379










      answered Dec 26 '11 at 17:54









      Davor Lucic

      21.9k75169




      21.9k75169








      • 5




        Is there be a solution with a custom lookup? docs.djangoproject.com/en/1.10/howto/custom-lookups It would cool to switch "__in" to "__all" and have it create the correct sql query.
        – t1m0
        Jan 18 '17 at 16:06






      • 1




        This annotation solution seems wrong. What if there are three tags possible (lets call the additional one for t3, and a photo has the tags t2 and t3. Then this photo will still match the given query.
        – beruic
        Apr 30 at 11:53










      • @beruic I think the idea is that you would replace num_tags=2 with num_tags=len(tags); I expect the hard-coded 2 was just for example's sake.
        – tbm
        May 2 at 17:29






      • 1




        @tbm It still would not work. Photo.objects.filter(tags__in=tags) matches photos that have any of the tags, not only those that has all. Some of those that only has one of the desired tags, may have exactly the amount of tags that you are looking for, and some of those that has all the desired tags, may also have additional tags.
        – beruic
        May 2 at 18:53










      • If your Django version is 1.8 or above you could use conditional annotations. See this answer: stackoverflow.com/a/33777815/1031534
        – beruic
        May 2 at 19:14














      • 5




        Is there be a solution with a custom lookup? docs.djangoproject.com/en/1.10/howto/custom-lookups It would cool to switch "__in" to "__all" and have it create the correct sql query.
        – t1m0
        Jan 18 '17 at 16:06






      • 1




        This annotation solution seems wrong. What if there are three tags possible (lets call the additional one for t3, and a photo has the tags t2 and t3. Then this photo will still match the given query.
        – beruic
        Apr 30 at 11:53










      • @beruic I think the idea is that you would replace num_tags=2 with num_tags=len(tags); I expect the hard-coded 2 was just for example's sake.
        – tbm
        May 2 at 17:29






      • 1




        @tbm It still would not work. Photo.objects.filter(tags__in=tags) matches photos that have any of the tags, not only those that has all. Some of those that only has one of the desired tags, may have exactly the amount of tags that you are looking for, and some of those that has all the desired tags, may also have additional tags.
        – beruic
        May 2 at 18:53










      • If your Django version is 1.8 or above you could use conditional annotations. See this answer: stackoverflow.com/a/33777815/1031534
        – beruic
        May 2 at 19:14








      5




      5




      Is there be a solution with a custom lookup? docs.djangoproject.com/en/1.10/howto/custom-lookups It would cool to switch "__in" to "__all" and have it create the correct sql query.
      – t1m0
      Jan 18 '17 at 16:06




      Is there be a solution with a custom lookup? docs.djangoproject.com/en/1.10/howto/custom-lookups It would cool to switch "__in" to "__all" and have it create the correct sql query.
      – t1m0
      Jan 18 '17 at 16:06




      1




      1




      This annotation solution seems wrong. What if there are three tags possible (lets call the additional one for t3, and a photo has the tags t2 and t3. Then this photo will still match the given query.
      – beruic
      Apr 30 at 11:53




      This annotation solution seems wrong. What if there are three tags possible (lets call the additional one for t3, and a photo has the tags t2 and t3. Then this photo will still match the given query.
      – beruic
      Apr 30 at 11:53












      @beruic I think the idea is that you would replace num_tags=2 with num_tags=len(tags); I expect the hard-coded 2 was just for example's sake.
      – tbm
      May 2 at 17:29




      @beruic I think the idea is that you would replace num_tags=2 with num_tags=len(tags); I expect the hard-coded 2 was just for example's sake.
      – tbm
      May 2 at 17:29




      1




      1




      @tbm It still would not work. Photo.objects.filter(tags__in=tags) matches photos that have any of the tags, not only those that has all. Some of those that only has one of the desired tags, may have exactly the amount of tags that you are looking for, and some of those that has all the desired tags, may also have additional tags.
      – beruic
      May 2 at 18:53




      @tbm It still would not work. Photo.objects.filter(tags__in=tags) matches photos that have any of the tags, not only those that has all. Some of those that only has one of the desired tags, may have exactly the amount of tags that you are looking for, and some of those that has all the desired tags, may also have additional tags.
      – beruic
      May 2 at 18:53












      If your Django version is 1.8 or above you could use conditional annotations. See this answer: stackoverflow.com/a/33777815/1031534
      – beruic
      May 2 at 19:14




      If your Django version is 1.8 or above you could use conditional annotations. See this answer: stackoverflow.com/a/33777815/1031534
      – beruic
      May 2 at 19:14













      5














      Another approach that works, although PostgreSQL only, is using django.contrib.postgres.fields.ArrayField:



      Example copied from docs:



      >>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
      >>> Post.objects.create(name='Second post', tags=['thoughts'])
      >>> Post.objects.create(name='Third post', tags=['tutorial', 'django'])

      >>> Post.objects.filter(tags__contains=['thoughts'])
      <QuerySet [<Post: First post>, <Post: Second post>]>

      >>> Post.objects.filter(tags__contains=['django'])
      <QuerySet [<Post: First post>, <Post: Third post>]>

      >>> Post.objects.filter(tags__contains=['django', 'thoughts'])
      <QuerySet [<Post: First post>]>


      ArrayField has some more powerful features such as overlap and index transforms.






      share|improve this answer


























        5














        Another approach that works, although PostgreSQL only, is using django.contrib.postgres.fields.ArrayField:



        Example copied from docs:



        >>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
        >>> Post.objects.create(name='Second post', tags=['thoughts'])
        >>> Post.objects.create(name='Third post', tags=['tutorial', 'django'])

        >>> Post.objects.filter(tags__contains=['thoughts'])
        <QuerySet [<Post: First post>, <Post: Second post>]>

        >>> Post.objects.filter(tags__contains=['django'])
        <QuerySet [<Post: First post>, <Post: Third post>]>

        >>> Post.objects.filter(tags__contains=['django', 'thoughts'])
        <QuerySet [<Post: First post>]>


        ArrayField has some more powerful features such as overlap and index transforms.






        share|improve this answer
























          5












          5








          5






          Another approach that works, although PostgreSQL only, is using django.contrib.postgres.fields.ArrayField:



          Example copied from docs:



          >>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
          >>> Post.objects.create(name='Second post', tags=['thoughts'])
          >>> Post.objects.create(name='Third post', tags=['tutorial', 'django'])

          >>> Post.objects.filter(tags__contains=['thoughts'])
          <QuerySet [<Post: First post>, <Post: Second post>]>

          >>> Post.objects.filter(tags__contains=['django'])
          <QuerySet [<Post: First post>, <Post: Third post>]>

          >>> Post.objects.filter(tags__contains=['django', 'thoughts'])
          <QuerySet [<Post: First post>]>


          ArrayField has some more powerful features such as overlap and index transforms.






          share|improve this answer












          Another approach that works, although PostgreSQL only, is using django.contrib.postgres.fields.ArrayField:



          Example copied from docs:



          >>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
          >>> Post.objects.create(name='Second post', tags=['thoughts'])
          >>> Post.objects.create(name='Third post', tags=['tutorial', 'django'])

          >>> Post.objects.filter(tags__contains=['thoughts'])
          <QuerySet [<Post: First post>, <Post: Second post>]>

          >>> Post.objects.filter(tags__contains=['django'])
          <QuerySet [<Post: First post>, <Post: Third post>]>

          >>> Post.objects.filter(tags__contains=['django', 'thoughts'])
          <QuerySet [<Post: First post>]>


          ArrayField has some more powerful features such as overlap and index transforms.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Jan 11 '17 at 13:34









          Sander van Leeuwen

          1,56511626




          1,56511626























              2














              This also can be done by dynamic query generation using Django ORM and some Python magic :)



              from operator import and_
              from django.db.models import Q

              categories = ['holiday', 'summer']
              res = Photo.filter(reduce(and_, [Q(tags__name=c) for c in categories]))


              The idea is to generate appropriate Q objects for each category and then combine them using AND operator into one QuerySet. E.g. for your example it'd be equal to



              res = Photo.filter(Q(tags__name='holiday') & Q(tags__name='summer'))





              share|improve this answer

















              • 2




                This would not work. Your query examples would not return anything for the models in question.
                – Davor Lucic
                Dec 26 '11 at 17:30










              • Thanks for correction. I thought chaining filter would be the same as using and for Q objects in one filter... My mistake.
                – demalexx
                Dec 27 '11 at 0:05










              • No worries, my first thought where also Q objects.
                – Davor Lucic
                Dec 27 '11 at 0:48






              • 1




                This would we slower if you work with large tables and large data to compare to. (like 1 Million each)
                – gies0r
                May 21 at 16:11
















              2














              This also can be done by dynamic query generation using Django ORM and some Python magic :)



              from operator import and_
              from django.db.models import Q

              categories = ['holiday', 'summer']
              res = Photo.filter(reduce(and_, [Q(tags__name=c) for c in categories]))


              The idea is to generate appropriate Q objects for each category and then combine them using AND operator into one QuerySet. E.g. for your example it'd be equal to



              res = Photo.filter(Q(tags__name='holiday') & Q(tags__name='summer'))





              share|improve this answer

















              • 2




                This would not work. Your query examples would not return anything for the models in question.
                – Davor Lucic
                Dec 26 '11 at 17:30










              • Thanks for correction. I thought chaining filter would be the same as using and for Q objects in one filter... My mistake.
                – demalexx
                Dec 27 '11 at 0:05










              • No worries, my first thought where also Q objects.
                – Davor Lucic
                Dec 27 '11 at 0:48






              • 1




                This would we slower if you work with large tables and large data to compare to. (like 1 Million each)
                – gies0r
                May 21 at 16:11














              2












              2








              2






              This also can be done by dynamic query generation using Django ORM and some Python magic :)



              from operator import and_
              from django.db.models import Q

              categories = ['holiday', 'summer']
              res = Photo.filter(reduce(and_, [Q(tags__name=c) for c in categories]))


              The idea is to generate appropriate Q objects for each category and then combine them using AND operator into one QuerySet. E.g. for your example it'd be equal to



              res = Photo.filter(Q(tags__name='holiday') & Q(tags__name='summer'))





              share|improve this answer












              This also can be done by dynamic query generation using Django ORM and some Python magic :)



              from operator import and_
              from django.db.models import Q

              categories = ['holiday', 'summer']
              res = Photo.filter(reduce(and_, [Q(tags__name=c) for c in categories]))


              The idea is to generate appropriate Q objects for each category and then combine them using AND operator into one QuerySet. E.g. for your example it'd be equal to



              res = Photo.filter(Q(tags__name='holiday') & Q(tags__name='summer'))






              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered Dec 26 '11 at 15:00









              demalexx

              3,13612331




              3,13612331








              • 2




                This would not work. Your query examples would not return anything for the models in question.
                – Davor Lucic
                Dec 26 '11 at 17:30










              • Thanks for correction. I thought chaining filter would be the same as using and for Q objects in one filter... My mistake.
                – demalexx
                Dec 27 '11 at 0:05










              • No worries, my first thought where also Q objects.
                – Davor Lucic
                Dec 27 '11 at 0:48






              • 1




                This would we slower if you work with large tables and large data to compare to. (like 1 Million each)
                – gies0r
                May 21 at 16:11














              • 2




                This would not work. Your query examples would not return anything for the models in question.
                – Davor Lucic
                Dec 26 '11 at 17:30










              • Thanks for correction. I thought chaining filter would be the same as using and for Q objects in one filter... My mistake.
                – demalexx
                Dec 27 '11 at 0:05










              • No worries, my first thought where also Q objects.
                – Davor Lucic
                Dec 27 '11 at 0:48






              • 1




                This would we slower if you work with large tables and large data to compare to. (like 1 Million each)
                – gies0r
                May 21 at 16:11








              2




              2




              This would not work. Your query examples would not return anything for the models in question.
              – Davor Lucic
              Dec 26 '11 at 17:30




              This would not work. Your query examples would not return anything for the models in question.
              – Davor Lucic
              Dec 26 '11 at 17:30












              Thanks for correction. I thought chaining filter would be the same as using and for Q objects in one filter... My mistake.
              – demalexx
              Dec 27 '11 at 0:05




              Thanks for correction. I thought chaining filter would be the same as using and for Q objects in one filter... My mistake.
              – demalexx
              Dec 27 '11 at 0:05












              No worries, my first thought where also Q objects.
              – Davor Lucic
              Dec 27 '11 at 0:48




              No worries, my first thought where also Q objects.
              – Davor Lucic
              Dec 27 '11 at 0:48




              1




              1




              This would we slower if you work with large tables and large data to compare to. (like 1 Million each)
              – gies0r
              May 21 at 16:11




              This would we slower if you work with large tables and large data to compare to. (like 1 Million each)
              – gies0r
              May 21 at 16:11











              0














              I use a little function that iterates filters over a list for a given operator an a column name :



              def exclusive_in (cls,column,operator,value_list):         
              myfilter = column + '__' + operator
              query = cls.objects
              for value in value_list:
              query=query.filter(**{myfilter:value})
              return query


              and this function can be called like that:



              exclusive_in(Photo,'tags__name','iexact',['holiday','summer'])


              it also work with any class and more tags in the list; operators can be anyone like 'iexact','in','contains','ne',...






              share|improve this answer


























                0














                I use a little function that iterates filters over a list for a given operator an a column name :



                def exclusive_in (cls,column,operator,value_list):         
                myfilter = column + '__' + operator
                query = cls.objects
                for value in value_list:
                query=query.filter(**{myfilter:value})
                return query


                and this function can be called like that:



                exclusive_in(Photo,'tags__name','iexact',['holiday','summer'])


                it also work with any class and more tags in the list; operators can be anyone like 'iexact','in','contains','ne',...






                share|improve this answer
























                  0












                  0








                  0






                  I use a little function that iterates filters over a list for a given operator an a column name :



                  def exclusive_in (cls,column,operator,value_list):         
                  myfilter = column + '__' + operator
                  query = cls.objects
                  for value in value_list:
                  query=query.filter(**{myfilter:value})
                  return query


                  and this function can be called like that:



                  exclusive_in(Photo,'tags__name','iexact',['holiday','summer'])


                  it also work with any class and more tags in the list; operators can be anyone like 'iexact','in','contains','ne',...






                  share|improve this answer












                  I use a little function that iterates filters over a list for a given operator an a column name :



                  def exclusive_in (cls,column,operator,value_list):         
                  myfilter = column + '__' + operator
                  query = cls.objects
                  for value in value_list:
                  query=query.filter(**{myfilter:value})
                  return query


                  and this function can be called like that:



                  exclusive_in(Photo,'tags__name','iexact',['holiday','summer'])


                  it also work with any class and more tags in the list; operators can be anyone like 'iexact','in','contains','ne',...







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 11 at 18:55









                  David

                  13




                  13























                      -1














                      If we want to do it dynamically, followed the example:



                      tag_ids = [t1.id, t2.id]
                      qs = Photo.objects.all()

                      for tag_id in tag_ids:
                      qs = qs.filter(tag__id=tag_id)

                      print qs





                      share|improve this answer





















                      • Cannot work as as soon as the second iteration, the queryset will be empty
                        – lapin
                        Jul 31 at 8:45
















                      -1














                      If we want to do it dynamically, followed the example:



                      tag_ids = [t1.id, t2.id]
                      qs = Photo.objects.all()

                      for tag_id in tag_ids:
                      qs = qs.filter(tag__id=tag_id)

                      print qs





                      share|improve this answer





















                      • Cannot work as as soon as the second iteration, the queryset will be empty
                        – lapin
                        Jul 31 at 8:45














                      -1












                      -1








                      -1






                      If we want to do it dynamically, followed the example:



                      tag_ids = [t1.id, t2.id]
                      qs = Photo.objects.all()

                      for tag_id in tag_ids:
                      qs = qs.filter(tag__id=tag_id)

                      print qs





                      share|improve this answer












                      If we want to do it dynamically, followed the example:



                      tag_ids = [t1.id, t2.id]
                      qs = Photo.objects.all()

                      for tag_id in tag_ids:
                      qs = qs.filter(tag__id=tag_id)

                      print qs






                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered May 5 at 8:16









                      tarasinf

                      618




                      618












                      • Cannot work as as soon as the second iteration, the queryset will be empty
                        – lapin
                        Jul 31 at 8:45


















                      • Cannot work as as soon as the second iteration, the queryset will be empty
                        – lapin
                        Jul 31 at 8:45
















                      Cannot work as as soon as the second iteration, the queryset will be empty
                      – lapin
                      Jul 31 at 8:45




                      Cannot work as as soon as the second iteration, the queryset will be empty
                      – lapin
                      Jul 31 at 8:45


















                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.





                      Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                      Please pay close attention to the following guidance:


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f8618068%2fdjango-filter-queryset-in-for-every-item-in-list%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Full-time equivalent

                      さくらももこ

                      13 indicted, 8 arrested in Calif. drug cartel investigation