子查询中的 Django 回合

Posted

技术标签:

【中文标题】子查询中的 Django 回合【英文标题】:Django round in subquery 【发布时间】:2021-01-16 23:29:27 【问题描述】:

您好,我正在使用以下代码获取带有子查询的 ListView 中每个项目的进度百分比

class projects(LoginRequiredMixin, ListView):
    model = Project
    template_name = 'project_list.html'
    ordering = ['project_title']
    paginate_by = 10
    queryset = Project.objects.annotate(
        todo_done=Count('todo', filter=Q(todo__state=True)) * 100 / Count('todo'),
        todo_left=Count('todo', filter=Q(todo__state=False)) * 100 / Count('todo'),
    )

在一个项目中,我有 12 个任务,8 个已完成,4 个正在进行中。系统返回 66% 已完成和 33% 正在进行中,总和是 99% 而不是 100%

 project.todo_done 
 project.todo_left 

栏的一部分是空白的,因为缺少 1%。我尝试按如下方式使用圆形,但这是不可能的

todo_done=round(Count('todo', filter=Q(todo__state=True)) * 100 / Count('todo')),
TypeError: type CombinedExpression doesn't define __round__ method

【问题讨论】:

【参考方案1】:

您不能使用 Python 的round,因为它不了解任何有关数据库表达式的内容。但是你可以使用Round expression [Django-doc]:

from django.db.models import F
from django.db.models.functions import Round

queryset = Project.objects.annotate(
    todo_done=Round(Count('todo', filter=Q(todo__state=True)) * 100 / Count('todo')),
    todo_left=100*Count('todo') - F('todo_done'),
)

如果Todos中的state只能是TrueFalse,最好将todo_doneCount('todo')中减去100次,因为这样通常可以保证两者将总计为 100,除非当然有没有相关的Todos。

如果两个操作数是整数,某些数据库会使用整数除法,您可以通过将其转换为 FloatField 来防止这种情况:

from django.db.models import F, FloatField
from django.db.models.functions import Cast, Round

queryset = Project.objects.annotate(
    todo_done=Round(
        Cast(Count('todo', filter=Q(todo__state=True)), output_field=FloatField())
        * 100 / Count('todo')
    ),
    todo_left=100*Count('todo') - F('todo_done'),
)

【讨论】:

查询与 todo 表相关,但即使在 todo_left 中减法保证最终总和为 100,我仍然看到它没有正确舍入。 ||| todo_done=Round(Count('todo', filter=Q(todo__state=True)) * 100 / Count('todo')), todo_left=100 - Round(Count('todo', filter=Q(todo__state=True) ) * 100 / Count('todo')), 我在本地尝试过,对于 mysql,它可以工作,对于 PostgreSQL,您可能需要转换为 FloatField,否则它会进行整数除法。 @Drennos:查看使用整数除法的数据库的编辑。 你的意思是把 BooleanField 改成 FloatField? @Drennos:不,将Todos 的Count(...) 转换为浮点数。

以上是关于子查询中的 Django 回合的主要内容,如果未能解决你的问题,请参考以下文章

聚合子查询的 FROM 子句中的项目必须引用更高级别 FROM 子句的嵌套表

子查询中的 Django 回合

Django 模型翻译查询回退

SQL 聚合子查询

如何使用 StringAgg 或 ArrayAgg 连接多个子行中的一列来注释 django 查询集?

如何提高其 SET 涉及昂贵的聚合子查询的 SQL UPDATE 语句的性能?