Django的.annotate()能返回对象吗?
我有一组记录,长得像这样(为了不让人觉得无聊,省略了一些多余的数据):
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
3 个回答
0
Annotate会返回一个查询集,但你同时调用了.values(),这总是会返回一个字典。
7
看起来我最开始提问的时候有些混淆,结果我被建议不要使用 .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()
可能也能解决这个问题,不过 在 Django 1.3 中,结合使用 .defer()
和 .annotate()
似乎有个 bug