Django query for many-to-many subset containment

跟風遠走 提交于 2019-12-04 03:20:30

A nice solution exists for Django >= 2.0. It is possible to annotate Aviaries by the number of matching Birds and to filter Aviaries that match at least one Bird or a required number.

from django.db.models import Count

    ...
    person_birds = set(self.birds_to_see.all())
    aviaries = (
        Aviary.objects
        .annotate(bird_match_count=Count('birds', filter=Q(birds__in=person_birds)))
        .filter(bird_match_count__gt=0)
    )

It is then trivial to filter a new queryset by bird_match_count=len(person_birds) or to filter the original queryset in Python or to sort it by bird_match_count.

Django < 2.0 would require to reference the intermediary model AviaryBirds and will be more verbose.


Verified by reading the SQL:

>>> print(aviaries.query)
SELECT aviary.id, aviary.name,
  COUNT(CASE WHEN  aviary_birds.bird_id IN (1,..)  THEN aviary_birds.bird_id ELSE NULL END)
    AS bird_match_count
FROM aviary LEFT OUTER JOIN aviary_birds ON (aviary.id = aviary_birds.aviary_id)
GROUP BY aviary.id, aviary.name
HAVING
  COUNT(CASE WHEN (aviary_birds.bird_id IN (1,..)) THEN aviary_birds.bird_id ELSE NULL END)
     > 0
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!