使用Django的ORM进行复杂的M2M过滤

时间:2019-11-17 09:41:59

标签: django django-models django-orm

我有以下型号:

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]

使用上述查询参数,由于2个M2M列表完全匹配,我只想返回Pizza 1。但是,我编写的查询返回了两个披萨。我该如何解决?感谢您的帮助。

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

1 个答案:

答案 0 :(得分:1)

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

因为披萨1的馅料为1和2,酱汁1和2都可以。
因为Pizza 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()

我不会太担心效率,因为既然QuerySet是惰性的,那么即使您的酱汁/浇头列表很大,您最终也会执行少量查询