在Django查询集中计算多个字段

8

我有一个模型,代表两个用户之间的交易,就像这样:

class Transaction(models.Model):
    buyer = models.ForeignKey(
        Person, on_delete=models.SET_NULL, null=True, related_name="bought"
    )
    seller = models.ForeignKey(
        Person, on_delete=models.SET_NULL, null=True, related_name="sold"
    )
    product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)

我希望能获取每个用户的交易次数(无论是作为买家还是卖家)。如果我只想统计一个字段,我只需执行以下操作:
Transaction.objects.values('seller').annotate(Count('seller'))

但是我无法在一个查询中同时处理两个字段。是否有方法可以实现? 谢谢。
3个回答

12

我刚刚遇到了这个问题,所以我会提供一个答案,以防将来有人需要: 显然的想法是在一个annotate中使用两个Count,但是根据Django文档的说法,在annotate中使用多个聚合将产生错误的结果。 对于Django 2.2或3,可以使用distinct关键字来使用Count

from django.db.models import Count
result = Person.objects.annotate(
    transaction_count=Count("bought", distinct=True) + Count("sold", distinct=True)
).values("id", "transaction_count")

对于 Django < 2.2 版本,您可以使用子查询:

from django.db.models import Count, OuterRef, F

buyer_subquery = (
    Transaction.objects.filter(buyer_id=OuterRef("id"))
    .values("buyer_id")
    .annotate(subcount=Count("id"))
    .values("subcount")
)
seller_subquery = (
    Transaction.objects.filter(seller_id=OuterRef("id"))
    .values("seller_id")
    .annotate(subcount=Count("id"))
    .values("subcount")
)
Person.objects.annotate(
    buyer_count=buyer_subquery,
    seller_count=seller_subquery,
    transaction_count=F("buyer_count") + F("seller_count"),
).values("id", "transaction_count")

谢谢您的回复 - 只是好奇,为什么每个计数都需要 distinct=True?我尝试了不加这个参数,但得到的模型总数很奇怪,无法解释。当我加上这个参数时,可以完美地将多个模型的计数相加。 - blueblob26
谢谢您的帮助,但是我不知道为什么我应该将“distinct”方法作为KWARG与annotate方法一起使用。当我使用它时,它打印出了所需的结果,但不理解它如何与annotate方法一起工作,所以您能否解释一下呢? - Abdelhamed Abdin
1
你有阅读我在回答中引用的Django文档吗?distinct使用SQL中的DISTINCT,可以防止重复计算相同的对象。如果没有DISTINCT,由于连接了多个表,这种情况可能会发生。这个Django问题解释了为什么这是一个问题。 - Lotram

0
也许像这样会起作用吗?
Transaction.objects.annotate(
    num_sellers=Count('seller'), num_buyers=Count('buyer')
)

这将返回两个计数器,而我想要的是两者的总和。如果用户'A'已经作为卖家进行了3次交易,并且作为买家进行了5次交易,我希望得到{'user': 'A', 'count': 8}。 - Lotram
请看这里:https://docs.djangoproject.com/en/1.11/topics/db/aggregation/#aggregating-annotations - noobinatrixxx

-1

过滤器可以引用模型上的字段¶

到目前为止,我们给出的示例中,构建了将模型字段的值与常量进行比较的过滤器。但是如果您想将模型字段的值与同一模型上的另一个字段进行比较怎么办?

Django提供了F表达式来允许这样的比较。 F()的实例在查询中充当对模型字段的引用。 然后可以在查询过滤器中使用这些引用来比较同一模型实例上两个不同字段的值.....

只需参考Django文档 https://docs.djangoproject.com/en/1.11/topics/db/queries/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接