Django 的 .annotate() 可以返回对象吗?

发布于 2024-11-02 20:51:26 字数 1426 浏览 0 评论 0原文

我有一个看起来像这样的记录集(省略了无关的数据以保护容易无聊的人):

 id | user_id |            created            | units
----+---------+-------------------------------+-------
  1 |       1 | 2011-04-18 15:43:02.737063+00 |    20
  2 |       1 | 2011-04-18 15:43:02.737063+00 |     4
  3 |       1 | 2011-04-18 15:46:48.592999+00 |    -1
  4 |       1 | 2011-04-19 12:02:10.687587+00 |    -1
  5 |       1 | 2011-04-19 12:09:20.039543+00 |    -1
  6 |       1 | 2011-04-19 12:11:21.948494+00 |    -1
  7 |       1 | 2011-04-19 12:15:51.544394+00 |    -1
  8 |       1 | 2011-04-19 12:16:44.623655+00 |    -1

并且我希望得到如下所示的结果:

 id | user_id |            created            | units
----+---------+-------------------------------+-------
  8 |       1 | 2011-04-19 12:16:44.623655+00 |    14
  2 |       1 | 2011-04-18 15:43:02.737063+00 |     4

所以我自然会寻找 .annotate()

u = User.objects.get(pk=1)
(MyModel.objects.filter(user=u)
        .values("id","user","created")
        .annotate(stuff=Sum("units"))
)

问题是我想要对象,而不是单个字典列表。我需要附加到这些对象的方法可用。关于如何做到这一点有什么想法吗?

编辑: 我应该指出我尝试使用 .values() 因为没有它我会得到一堆带注释的对象,但是其中会有 8 个(如上面的第一个查询中所示),而不是 2 个(如第二个查询中所示)结果如上)。我的猜测是,它没有合并行,因为其中有一个时间戳使行不同:

MyModel.objects.filter(user=u).annotate(Sum("units"))
# Returns 8 records, not 2 as expected

I have a record set that looks like this (extraneous data omitted to protect the easily bored):

 id | user_id |            created            | units
----+---------+-------------------------------+-------
  1 |       1 | 2011-04-18 15:43:02.737063+00 |    20
  2 |       1 | 2011-04-18 15:43:02.737063+00 |     4
  3 |       1 | 2011-04-18 15:46:48.592999+00 |    -1
  4 |       1 | 2011-04-19 12:02:10.687587+00 |    -1
  5 |       1 | 2011-04-19 12:09:20.039543+00 |    -1
  6 |       1 | 2011-04-19 12:11:21.948494+00 |    -1
  7 |       1 | 2011-04-19 12:15:51.544394+00 |    -1
  8 |       1 | 2011-04-19 12:16:44.623655+00 |    -1

And I'd like to get a result that looks like this:

 id | user_id |            created            | units
----+---------+-------------------------------+-------
  8 |       1 | 2011-04-19 12:16:44.623655+00 |    14
  2 |       1 | 2011-04-18 15:43:02.737063+00 |     4

So I naturally look to .annotate():

u = User.objects.get(pk=1)
(MyModel.objects.filter(user=u)
        .values("id","user","created")
        .annotate(stuff=Sum("units"))
)

The problem is that I want objects, not a single list of dictionaries. I need the methods attached to those objects to be available. Any ideas as to how to do this?

Edit:
I should have pointed out that I tried using .values() because without it I would get a bunch of annotated objects alright, but there would be 8 of them (as in the first query above), and not 2 (as in the second result above). My guess is that it's not combining the rows because there's a timestamp in there making the rows different:

MyModel.objects.filter(user=u).annotate(Sum("units"))
# Returns 8 records, not 2 as expected

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦里南柯 2024-11-09 20:51:26

当我最初提出这个问题时,看起来似乎有些混乱,因此当这实际上不是问题时,我被指示不要使用 .values() 。问题是 Django 的 .annotate() 不允许用计算值覆盖列属性,而只用附加数据注释对象。这确实是有道理的,因为如果您想要返回一个对象,您希望能够假设该对象实际上代表数据库中的一行,而不是覆盖列值的计算值的混杂。

但是,这对我来说不起作用,因为上述功能意味着对于像 created (时间戳)这样的列,如果不使用 .values() 就无法获得我想要的计算值...这并没有给我对象。

所以,我选择了下一个最好的事情: .raw()

query = """
    SELECT
        ep.product_id              AS product_id,
        ep.user_id                 AS user_id,
        MAX(ep.created)            AS created,
        SUM(eup.units)             AS units
    FROM
        ecom_unitpermission AS eup
    WHERE
        ep.user_id = 1
    GROUP BY
        ep.product_id,
        ep.user_id
"""

for perm in MyModel.objects.raw(query):
    print "%15s %3s %s" % (perm.product.name, perm.units, perm.product.somemethod())

使用 .defer() 可能已经为我做到了这一点,尽管 .annotate() 结合使用时,Django 1.3 中似乎存在错误

It looks like there was some confusion when I initially asked the question, and as a result I was directed to not use .values() when that wasn't actually the problem. The problem is that Django's .annotate() doesn't allow for the overwriting of column properties with calculated values, and only annotates objects with additional data. This makes sense really, since if you want an object returned, you want to be able to assume that the object in question actually represents a row in the database, and not a mishmash of calculated values overwriting column values.

However, this didn't work for me, because the above functionality means that with columns like created (a timestamp) you can't get the calculated values I wanted without using .values()... and that doesn't give me objects.

So, I opted for the next best thing: .raw():

query = """
    SELECT
        ep.product_id              AS product_id,
        ep.user_id                 AS user_id,
        MAX(ep.created)            AS created,
        SUM(eup.units)             AS units
    FROM
        ecom_unitpermission AS eup
    WHERE
        ep.user_id = 1
    GROUP BY
        ep.product_id,
        ep.user_id
"""

for perm in MyModel.objects.raw(query):
    print "%15s %3s %s" % (perm.product.name, perm.units, perm.product.somemethod())

Using .defer() might have done this for me, though there appears to be a bug in Django 1.3 when combining that with .annotate()

懷念過去 2024-11-09 20:51:26

问题不在于注释,而在于对值的调用。 文档非常清楚地表明注释适用于查询集。值调用会返回值字典,并使您无法访问模型。

编辑添加:如果您只想恢复某些字段但仍然有模型,则使用 defer 而不是值

The problem isn't annotate but the call to values. The documentation quite clearly show that annotate works on querysets. It is the values call that returns the dictionary of values and loses you the ability to access the models.

Edit to add: If you only want to bring back certain fields but still have models then use defer instead of values

擦肩而过的背影 2024-11-09 20:51:26

Annotate 返回一个查询集,但您还调用 .values() ,它将始终返回一个字典。

Annotate returns a queryset, but you're also calling .values() which will always return a dictionary.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文