Filtering on many-to-many relations that fulfill a set of criteria

时光毁灭记忆、已成空白 提交于 2019-11-28 02:17:37

A simple solution:

order_operations = OrderOperation.objects.all()
for article in articles:
    order_operations = order_operations.filter(ordered_articles=article)

It's just one query, but with an inner join per article. For more than a few articles Willem’s more ingenious solution should perform better.

We can first construct a set of articles:

articles_set = set(articles)

Next we can count the number of articles related to the OrderOperation that appear in that set, and check if that number is equal to the size of that set, like:

from django.db.models import Count

OrderOperation.objects.filter(
    ordered_articles__in=articles_set
).annotate(
    narticles=Count('ordered_articles')
).filter(
    narticles=len(articles_set)
)

Since in a ManyToManyField, each Article can occur once per OrderOperation, if the number of related Articles that are in the article_set is the same as the number of elements in the article_set, we thus know that the two sets are the same.

This will create a query that looks like:

SELECT orderoperation.*
       COUNT(orderoperation_article.article_id) AS narticle
FROM orderoperation
JOIN orderoperation_article ON orderoperation_id = orderoperation.id
WHERE orderoperation.article_id IN (article_set)
GROUP BY orderoperation.id
HAVING COUNT(orderoperation_article.article_id) = len(article_set)

where the article_set and len(article_set) are of course replaced by the primary keys of the articles in the set, or the number of elements in that set.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!