【问题标题】:Advanced django queries高级 django 查询
【发布时间】:2025-12-06 07:10:01
【问题描述】:

我有三个模型:公司、资产和用户。每个公司都有很多资产(ForeignKey),每个资产都有一个用户(ForeignKey,因为一个用户可能拥有多个资产)。

我想运行一个查询,返回给定用户拥有资产的公司列表,以及他们在每家公司拥有的资产数量。

我曾尝试在 python 中这样做,如下所示:

companies = Company.objects.filter(asset__owner=self.request.user)
context['investments'] = [(x, Asset.objects.filter(company=x, owner=self.request.user).count()) for x in companies] 

这可能并不令人惊讶,因为一家公司可以拥有 100,000 项资产。似乎在数据库级别这样做会更好,这导致我查看注释和聚合,但我无处可去。有人能指出我正确的方向吗?

【问题讨论】:

标签: django django-models django-queryset


【解决方案1】:

您可以使用条件进行注释:

from django.db.models import Count, Case, When, IntegerField

qs = Company.objects.filter(asset__owner=self.request.user)
qs = qs.objects.annotate(
    num_assets=Count(Case(
        When(asset__owner=self.request.user, then=1),
        output_field=IntegerField(),
    ))
)

阅读文档conditional-aggregation

【讨论】:

  • 谢谢!你能看看迈克尔罗伯茨刚刚发布的答案吗?有什么问题吗?它似乎有效,但您发布的内容更像我的预期。
  • @Alex 这看起来会更有效率……看看你是否可以运行一些性能测试,看看哪个会更快!
  • 正如我在您的回答中看到的,将计算所有资产包括其他用户。
  • @BearBrown 嗯,这就是我所期望的,但它没有,它似乎工作
  • @Alex - 如果您处于预生产阶段,您确定您的数据库中有多个用户吗?听起来很疯狂 - 我已经这样做了,认为它工作正常,但是添加多个用户并且您的 Query 会稍微消失......
【解决方案2】:

您可以通过.filter(..).annotate(..) 的组合来做到这一点:

from django.db.models import Count

Company.objects.filter(
    asset__owner=some_user
).annotate(
    num_assets=Count('asset')
)

所以这里我们返回一个包含Company 对象的QuerySet,每个对象都有一个额外的属性:num_assets,它包含Company 中给定用户拥有的资产数量。

从这里开始 - 如果我理解正确 - 资产始终属于单一公司,没有重复计算:如果它是多对- many 关系,即属于两家公司的用户所拥有的 assed,会增加两家公司的计数。

因此,查询集返回用户拥有资产的公司,Companys 没有用户拥有的任何资产,因此被过滤掉。 p>

【讨论】:

  • 谢谢。事实上,每项资产都属于一家公司。我查看了上面的内容,但假设这会给我:(根据需要)包含用户拥有的至少一项资产的公司列表,但注释将给出该公司拥有的资产总数(是否它们是否归用户所有)...
  • 这对我来说也适用于快速应用程序床。所有答案似乎都很好@Alex