在Django中的ValuesQuerySet上使用extra()

时间:2011-03-04 11:03:00

标签: sql django django-queryset extra

我正在尝试使用两个本身聚合的值来计算百分比。解释我所追求的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

3 个答案:

答案 0 :(得分:3)

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很快就被文件说明关闭,因为值()之后的额外()不起作用 - commit a4a250a。)

答案 1 :(得分:0)

正如您所指出的(#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')]

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

答案 2 :(得分:0)

  

如果在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')