在 Django 中的 ValuesQuerySet 上使用 extra()

Posted

技术标签:

【中文标题】在 Django 中的 ValuesQuerySet 上使用 extra()【英文标题】:Using extra() on ValuesQuerySet in Django 【发布时间】:2011-07-08 18:00:55 【问题描述】:

我正在尝试用两个本身聚合的值来计算百分比。解释我所追求的 SQL 查询如下:

SELECT (SUM(field_a) / SUM(field_b) * 100) AS percent
FROM myapp_mymodel 
GROUP BY id
ORDER BY id

我尝试使用以下内容来构造一个 QuerySet,但不幸的是它不包含额外的字段:

MyModel.objects.values('id').annotate(
   sum_field_a=Sum('field_a'),
   sum_field_b=Sum('field_b')).extra(
      select='percent': 'sum_field_a / sum_field_b * 100')

让我恼火的是——根据 Django 文档——这似乎是要走的路:

当使用 values() 子句时 约束列 在结果集中返回 […] 而不是返回 每个结果的注释结果 原来的QuerySet,原来的 结果根据分组 字段的独特组合 在 values() 子句中指定。一个 然后为每个提供注释 独特的群体;注释是 计算所有成员的 组。

来源: http://docs.djangoproject.com/en/dev/topics/db/aggregation/#values

如果在 extra() 子句之后使用 values() 子句,则 extra() 中的 select 参数定义的任何字段都必须显式包含在 values() 子句中。但是,如果在 values() 之后使用 extra() 子句,则 select 添加的字段将自动包含在内。

来源:http://docs.djangoproject.com/en/dev/ref/models/querysets/#values

【问题讨论】:

【参考方案1】:

Aggregate expressions 可以轻松地在聚合函数上使用此类表达式since Django 1.8,而无需使用有问题的“extra()”方法。

qs = (
    MyModel.objects.values('id')
    .annotate(percent=Sum('field__a') / Sum('field__b') * 100)
    .order_by('id')
)
>>> print(str(qs.query))
SELECT id, ((SUM(field_a) / SUM(field_b)) * 100) AS percent
FROM app_mymodel GROUP BY id ORDER BY id ASC

(所提到的问题 #15546 已被文档说明很快关闭,即 values() 之后的 extra() 将不起作用 - commit a4a250a。)

【讨论】:

我还没有对此进行测试,因为我没有再调查过这个问题。但是由于聚合表达式似乎是解决这个问题的方法,所以我将此答案标记为我的问题的解决方案。 我还尝试使用 ForeignKey 上的组或从反向关系端开始的查询集来检查 SQL。一个简单的例子是为了回答最好的。【参考方案2】:

正如您所指出的 (#15546),django 中可能存在错误。

但作为一种解决方法,您可以通过执行以下操作将实际计算的负担放在 python 而不是 SQL 数据库上:

['field_c': model['field_c'],
  'percent': m['sum_field_a'] * 100.0 / m['sum_field_b']
 for model in MyModel.objects.values('field_c').annotate(
    sum_field_a=Sum('field_a'),
    sum_field_b=Sum('field_b')).order_by('field_c')]

由于此解决方案强制您遍历所有数据,具体取决于您想要执行的操作,它可能会或可能不会被接受。

【讨论】:

谢谢。我希望能在 python 中解决这个问题,但这可能是在 bug 得到进一步开发之前要走的路。【参考方案3】:

如果在 extra() 子句之后使用 values() 子句,则 extra() 中的 select 参数定义的任何字段都必须显式包含在 values() 子句中。

来源:http://docs.djangoproject.com/en/dev/ref/models/querysets/#values

在 select 中添加的 'percent' 字段可以显式添加到 values 子句中,并且应该添加到查询集中。

MyModel.objects.annotate(
              sum_field_a=Sum('field_a'),
              sum_field_b=Sum('field_b')).extra(
              select='percent': 'sum_field_a / sum_field_b * 100'
         ).values('id', 'percent')

【讨论】:

结果需要在注解之前进行分组(因此values()annotate()之前)。此外,当我尝试选择通过注释创建的字段时,我得到 Unknown column field_a 已编辑 sn-p 添加 put 'id' 代替 field_c 现在试试?

以上是关于在 Django 中的 ValuesQuerySet 上使用 extra()的主要内容,如果未能解决你的问题,请参考以下文章

使用 django-import-export 在 django 迁移中的外键

django中的信号

Django 1.7 中的 Django-migrations 检测模型更改,但不会在迁移时应用它们

Django:为啥我放置在 Django Summernote 中的文本会在我的 HTML 模板中显示 HTML 标记?

Django 中的 % % 和

javascript中的Django模板值