我的模型类似于Django文档的Pizza示例:
class Pizza(models.Model):
name = models.CharField()
toppings = models.ManyToManyField('Topping')
def __str__(self):
return self.name
class Topping(models.Model):
name = models.CharField()
def __str__(self):
return self.name
以及一些披萨的浇头:
>>> pepperoni = Topping.objects.create(name='pepperoni')
>>> sausage = Topping.objects.create(name='sausage')
>>> pineapple = Topping.objects.create(name='pineapple')
>>> olives = Topping.objects.create(name='olives')
>>> p1 = Pizza.objects.create(name='Pepperoni')
>>> p1.toppings.add(pepperoni)
>>> p2 = Pizza.objects.create(name='Sausage')
>>> p2.toppings.add(sausage)
>>> p3 = Pizza.objects.create(name='Pepperoni and Sausage')
>>> p3.toppings.add(pepperoni)
>>> p3.toppings.add(sausage)
>>> p4 = Pizza.objects.create(name='Pepperoni and Olives')
>>> p4.toppings.add(pepperoni)
>>> p4.toppings.add(olives)
>>> p5 = Pizza.objects.create(name='Pepperoni and Sausage and Olives')
>>> p5.toppings.add(pepperoni)
>>> p5.toppings.add(sausage)
>>> p5.toppings.add(olives)
>>> ...
我如何创建一个查询,该查询将仅返回带有意大利辣香肠(p1
)或香肠(p2
)或意大利辣香肠或香肠(p3
的披萨)?我不想要包含意大利辣香肠,香肠和其他东西(p5
)的比萨。
类似这样的东西将包括一个比萨饼,其中包含意大利辣香肠和橄榄(p4
),我不想要:
>>> Pizza.objects.filter(toppings__in=[pepperoni, sausage])
我可以创建除我想要的两个浇头之外的所有浇头的列表,并将其用作排除项:
>>> toppings_i_do_not_want = Topping.objects.exclude(name__in=['Pepperoni', ['Sausage'])
>>> toppings_i_want = Topping.objects.filter(name__in=['Pepperoni', ['Sausage'])
>>> Pizza.objects.filter(toppings__in=toppings_i_want).exclude(toppings_i_do_not_want)
这将导致我想要的结果,但是如果我仅对两个浇头感兴趣,但必须将〜100,000个其他浇头传递到排除过滤器中,则这样的查询的性能将遭受极大的损害。
有更好的方法吗?
答案 0 :(得分:0)
我们可以计算pepperoni
或sausage
的浇头数量,并将其与相关浇头的总数进行比较(如果两者匹配,且数量大于0
) ,那么我们可以退回这样的比萨饼:
from django.db.models import Count, Q
Pizza.objects.annotate(
ntopping=Count('toppings')
).filter(
ntopping__gte=1,
ntopping=Count('toppings', filter=Q(toppings__in=[pepperoni, sausage]))
)
将完全按照您的要求做。它将返回Pizza
条记录,其记录“列表[pepperoni, sausage]
中存在相关的开头”。因此,对于具有pepperoni
浇头,sausage
浇头或两个浇头的比萨饼。