【问题标题】:Complex M2M filtering using Django's ORM使用 Django 的 ORM 进行复杂的 M2M 过滤
【发布时间】:2019-11-17 09:41:59
【问题描述】:

我有以下型号:

class Sauce(models.Model):
    ...

class Topping(models.Model):
    ...

class Pizza(models.Model):
    sauces = models.ManyToManyField(Sauce, related_name='pizzas')
    toppings = models.ManyToManyField(Topping, related_name='pizzas')

现在,假设我想查询所有给定配料和酱汁列表的比萨饼。例如:

sauces_ids = [1, 2]
toppings_ids = [1, 2]

我现在在我的 API 视图中正在做的事情如下:

pizzas = Pizza.objects.filter(restaurant=restaurant)

if request.data.get('sauces_ids', []):
    pizzas = pizzas.filter(
        sauces__in=
        request.data['sauces_ids']
    )

if request.data.get('toppings_ids', []):
    pizzas = pizzas.filter(
        toppings__in=
        request.data['toppings_ids']
    )

return pizzas.distinct()

我使用distinct() 函数解决了一个重复问题。但是,现在我面临一个不同的问题。我的数据库中有 2 个比萨饼:

  • 比萨 1 加酱汁 = [1, 2],浇头 = [1, 2]
  • 披萨 2 加酱汁 = [1, 2],浇头 = [1]

使用上面的查询参数,我想只返回 Pizza 1,因为 2 个 M2M 列表完全匹配。但是,我写的查询是返回两个比萨饼。我该如何解决这个问题?感谢您的帮助。

此外,这是一种有效的方法吗?

【问题讨论】:

    标签: django django-models django-orm


    【解决方案1】:

    这是因为您正在执行的查询将返回具有顶部 1 或顶部 2 AND 酱 1 或酱 2 的比萨饼。

    因为披萨 1 有浇头 1 和 2,以及酱汁 1 和 2,所以你得到了它。
    因为比萨 2 有浇头 1,还有酱汁 1 和 2。

    基本上,如果我没记错的话,您可以执行以下操作:

    pizzas = Pizza.objects.filter(restaurant=restaurant)
    
    if request.data.get('sauces_ids', []):
        sauces = request.data.get('sauces_ids'):
        for sauce in sauces:
            pizzas = pizzas.filter(
                sauces__pk=sauce
            )
    
    if request.data.get('toppings_ids', []):
        toppings = request.data.get('toppings_ids'):
        for topping in toppings:
            pizzas = pizzas.filter(
                toppings__pk=topping
            )
    
    return pizzas.distinct()
    

    我不会太担心效率,因为 QuerySets 是惰性的,即使你的调味料/顶部列表很大,你最终也会执行少量查询

    【讨论】:

      猜你喜欢
      • 2014-09-24
      • 2016-04-17
      • 1970-01-01
      • 1970-01-01
      • 2012-02-29
      • 1970-01-01
      • 1970-01-01
      • 2010-10-13
      • 1970-01-01
      相关资源
      最近更新 更多