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

后端 未结 2 1204
借酒劲吻你
借酒劲吻你 2020-12-07 06:01

With the following models:

class OrderOperation(models.Model):
    ordered_articles = models.ManyToManyField(Article,
                                                


        
相关标签:
2条回答
  • 2020-12-07 06:10

    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.

    0 讨论(0)
  • 2020-12-07 06:27

    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.

    0 讨论(0)
提交回复
热议问题