简体   繁体   English

Django QuerySet 使用子查询进行注释

[英]Django QuerySet annotate with Subquery

given the following model给出以下 model

class Pizza(Model):
  pass

class Topping(Model):
  on = ManyToMany(Pizza, related_name='on_pizza')

I'm trying to get, my Pizza and the number of toppings, along with the top 3 other pizzas (top in terms of the number of toppings) The final result should be: a list containing the current pizza, and the top 3 pizzas, along with the count of toppings for all.我正在尝试获取我的比萨饼和浇头数量,以及前 3 个其他比萨饼(就浇头数量而言排名第一)最终结果应该是:包含当前比萨饼和前 3 个比萨饼的列表,以及所有浇头的数量。

So right now I have a pizza, and I have the top3 pizzas (top in terms of num of toppings)所以现在我有一个披萨,我有前三名的披萨(就配料数量而言排名第一)

But I have to iterate over the top pizzas to create a dict但我必须遍历顶级比萨来创建一个字典

for pizza in top_pizzas:
  data.append({'pizza':pizza, 'num':pizza.tcount})

<- this is what I'm trying to avoid, I don't want to do a query in a loop, I want to fetch the current pizza and its number of toppings, and also fetch the top3 pizzas and their number of toppings, all in one query. <- 这是我要避免的,我不想在循环中进行查询,我想获取当前的比萨饼及其浇头数量,并获取 top3 比萨饼及其浇头数量,一站式查询。

to get the other top pizzas I'm doing:得到我正在做的其他顶级披萨:

top = Pizza.objects.all().exclude(pk=self.pk).annotate(tcount=Count('on_pizza')).order_by('-on_pizza')

this is in a class function (on the model)这是在 class function 中(在模型上)

What I want is the above query, including the current pizza, this is what I tried:我想要的是上面的查询,包括当前的披萨,这是我尝试过的:

    def compared_with_top(self):
      top = Pizza.objects.all().exclude(pk=self.pk).annotate(tcount=Count('on_pizza')).order_by('-tcount')
      me_with_top = Pizza.objects\
          .filter(pk=self.pk)\
          .annotate(tcount=Count('on_pizza'))\
          .annotate(other_top_3=Subquery(top[:3]))
      return me_with_top

This gives me an error: FieldError: Cannot resolve expression type, unknown output_field这给了我一个错误: FieldError: Cannot resolve expression type, unknown output_field

I've noticed all the example of subquery involve 2 separate models, and use OuterRef, my query doesn't have an outer ref (its all the same model) I just want to get 2 queries in one if that makes since.我注意到子查询的所有示例都涉及 2 个单独的模型,并且使用 OuterRef,我的查询没有外部 ref(它都是相同的模型),如果这样的话,我只想将 2 个查询合二为一。

The above error points to 'output_field' but I cant find any information about what that should be.上面的错误指向'output_field',但我找不到任何关于它应该是什么的信息。

Edit: Someone suggested I do a union of the 2 queries, which sounded right, but then I get an error DatabaseError: ORDER BY not allowed in subqueries of compound statements.编辑:有人建议我合并 2 个查询,这听起来不错,但随后我收到错误DatabaseError: ORDER BY not allowed in subqueries of compound statements.

EDIT#2: The above error only occurs on SQLite EDIT#2:上述错误仅发生在 SQLite

You can make a .union(…) [Django-doc] to construct the uion of the two querysets.您可以制作一个.union(…) [Django-doc]来构造两个查询集的 uion。 This is thus:这是这样的:

def compared_with_top(self):
    top = Pizza.objects.exclude(pk=self.pk).annotate(
        tcount=Count('on_pizza')
    ).order_by('-tcount')
    pizzas = Pizza.objects.filter(pk=self.pk).annotate(
        tcount=Count('on_pizza')
    ).union(top[:3])

@ReedJones: tested this. @ReedJones:对此进行了测试。 If you run this on , then this will error.如果你在上运行它,那么这将出错。 Probably for the other databases, this will run fine.可能对于其他数据库,这将运行良好。

If these were your pizzas:如果这些是你的披萨:

Pizza.objects.values()
Out[15]: <QuerySet [
    {'id': 1, 'name': 'Hawaiian'},
    {'id': 2, 'name': 'Cheese'},
    {'id': 3, 'name': 'Veggie'},
    {'id': 4, 'name': 'Meat Lovers'},
    {'id': 5, 'name': 'Pineapple ONLY'}
]>

and these were your toppings:这些是你的配料:

Topping.objects.all()
Out[17]: <QuerySet [
    <Topping: Canadian Bacon>, <Topping: Pineapple>, <Topping: Cheese>,
    <Topping: Green peppers>, <Topping: Olives>, <Topping: Mushrooms>,
    <Topping: Onions>, <Topping: Tomatoes>, <Topping: Sausage>,
    <Topping: Pepperoni>, <Topping: Beef>
]>

and this was your model:这是你的 model:

from django.db import models
from django.db.models import Q, Count

class Pizza(models.Model):
    name = models.CharField(max_length=50)
    toppings = models.ManyToManyField('Topping', related_name='on_pizza')

    def __str__(self):
        return self.name

    def compared_with_top(self):
        top = Pizza.objects.annotate(Count('toppings')).order_by(
            '-toppings__count').exclude(id=self.id)[:3]
        return Pizza.objects.filter(Q(id=self.id) | Q(id__in=top.values('id')))
        

class Topping(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name

What the count of toppings looks like per pizza:每个披萨的浇头数量是多少:

In [32]: Pizza.objects.annotate(Count('toppings')).values()
Out[32]: <QuerySet [
{'id': 1, 'name': 'Hawaiian', 'toppings__count': 2},
{'id': 2, 'name': 'Cheese', 'toppings__count': 1},
{'id': 3, 'name': 'Veggie', 'toppings__count': 5},
{'id': 4, 'name': 'Meat Lovers', 'toppings__count': 6},
{'id': 5, 'name': 'Pineapple ONLY', 'toppings__count': 1}
]>

We make some pizzas:我们做一些比萨饼:

hawaiian = Pizza.objects.get(name='Hawaiian')
cheese = Pizza.objects.get(name='Cheese')
veggie = Pizza.objects.get(name='Veggie')
meat = Pizza.objects.get(name='Meat Lovers')
pineapple = Pizza.objects.get(name='Pineapple ONLY')

Pizza.compared_with_top based on count results: Pizza.compared_with_top基于计数结果:

In [26]: hawaiian.compared_with_top()
Out[26]: <QuerySet [<Pizza: Hawaiian>, <Pizza: Cheese>, <Pizza: Veggie>, <Pizza: Meat Lovers>]>

In [27]: cheese.compared_with_top()
Out[27]: <QuerySet [<Pizza: Cheese>, <Pizza: Hawaiian>, <Pizza: Veggie>, <Pizza: Meat Lovers>]>

In [28]: veggie.compared_with_top()
Out[28]: <QuerySet [<Pizza: Veggie>, <Pizza: Hawaiian>, <Pizza: Cheese>, <Pizza: Meat Lovers>]>

In [29]: meat.compared_with_top()
Out[29]: <QuerySet [<Pizza: Meat Lovers>, <Pizza: Hawaiian>, <Pizza: Cheese>, <Pizza: Veggie>]>

In [30]: pineapple.compared_with_top()
Out[30]: <QuerySet [<Pizza: Pineapple ONLY>, <Pizza: Hawaiian>, <Pizza: Veggie>, <Pizza: Meat Lovers>]>

This gets you the right output with the first object in the queryset being the current pizza.这将为您提供正确的 output ,其中查询集中的第一个 object 是当前的披萨。 The three that follow are not sorted from most to least though.不过,后面的三个不是从高到低排序的。 It's backwards.它是倒退的。 I'm not sure why they are being sorted least to greatest but maybe someone else does know.我不确定为什么它们被排序为最小到最大,但也许其他人确实知道。 I suspect it's because of using Q .我怀疑这是因为使用Q

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM