【问题标题】:Can Django's .annotate() return objects?Django 的 .annotate() 可以返回对象吗?
【发布时间】:2011-04-19 14:44:14
【问题描述】:

我有一个如下所示的记录集(省略了无关数据以保护容易无聊的人):

 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

【问题讨论】:

  • 对于问题的第二部分,即注释,我不太清楚第一组数据如何导致第二组数据...为什么 ids 8 和 2 剩下的总和为 14和4? Annotate 不会减少返回的行数。也许如果你添加了你想要实现的 SQL
  • 你用来生成上面显示的结果表(2行)的普通sql查询是什么

标签: python django annotate


【解决方案1】:

当我最初提出这个问题时,似乎有些困惑,因此我被指示不要使用.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() 可能已经为我做到了,尽管there appears to be a bug in Django 1.3 when combining that with .annotate()

【讨论】:

    【解决方案2】:

    问题不在于注释,而在于对值的调用。 documentation 非常清楚地表明注释适用于查询集。正是 values 调用返回了值字典并让您无法访问模型。

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

    【讨论】:

    • 实际上,我添加了 .values() 因为没有它,我无法将结果聚合到我期望的两行中。我认为这与我在那里有一个时间戳有关,所以行都是不同的。
    【解决方案3】:

    Annotate 返回一个查询集,但你也调用了 .values() ,它总是返回一个字典。

    【讨论】:

      猜你喜欢
      • 2018-09-22
      • 1970-01-01
      • 1970-01-01
      • 2015-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多