在 django orm 中聚合多对多

Posted

技术标签:

【中文标题】在 django orm 中聚合多对多【英文标题】:aggregate on many to many in django orm 【发布时间】:2021-03-04 05:42:14 【问题描述】:

我想创建一份顾问在本月建议的持续时间总和的报告。 我的模型:

class Adviser(models.AbstractBaseModel):
    user = models.OneToOneField('accounts.User', on_delete=models.PROTECT, related_name='adviser')
    patients = models.ManyToManyField('patient.Patient', through='adviser.AdviserPatient', related_name='advisers')

class AdviserPatient(models.AbstractBaseModel):
    adviser = models.ForeignKey('adviser.Adviser', on_delete=models.PROTECT, related_name='adviser_patient')
    patient = models.ForeignKey('patient.Patient', on_delete=models.PROTECT, related_name='adviser_patient')
    duration = models.SmallIntegerField()
    assign_date = models.DateField(auto_now_add=True)
    release_date = models.DateField(null=True, blank=True)

class Patient(models.AbstractBaseModel):
    user = models.OneToOneField('accounts.User', on_delete=models.PROTECT, related_name='patient')

我的查询:

ended_advise_this_mouth = Adviser.objects.annotate(
                total=Case(When(
                    adviser_patient__release_date__gte=start_of_month(),
                    adviser_patient__release_date__lte=end_of_month(),
                    then=Sum('adviser_patient__duration')), default=Value(0), output_field=IntegerField()))

但是通过这个查询,我得到了这样的重复顾问:

<QuerySet [<Adviser: [1 None None]>, <Adviser: [1 None None]>, <Adviser: [1 None None]>, <Adviser: [1 None None]>, <Adviser: [1 None None]>, <Adviser: [1 None None]>, <Adviser: [2 vahid imanian]>]>

如您所见,顾问 1 重复 6 次,总计单独。 当我使用方法 values('id') 或使用 distinct() 时,结果不受影响。我的数据库是 mysql,不能使用 distinct('id')。 我需要一个用于传递序列化程序的查询集,请帮助我修复此查询并 有没有办法为此查询集使用 django-rest-framework 序列化程序?

【问题讨论】:

【参考方案1】:

通过使用CaseWhen,对于每个adviser_patient 行,它将检查它是否在范围内,并使用该单行的Sum(因此是持续时间本身),或0 在如果超出范围。

因此您需要聚合更高的级别:

from django.db.models import IntegerField, Q, Sum, Value
from django.db.models.functions import Coalesce

# since Django-2.0
ended_advise_this_mouth = Adviser.objects.annotate(
    total=Coalesce(Sum(
        'adviser_patient__duration',
        filter=Q(
            adviser_patient__release_date__gte=start_of_month(),
            adviser_patient__release_date__lte=end_of_month()
        ),
        output_field=IntegerField()
    ), Value(0))
)

【讨论】:

感谢您的回答,我如何计算本月建议的持续时间。它的意思是可能在上个月开始(本月的持续时间必须计算),它的开始和结束在本月,从本月开始和下个月结束(本月的持续时间必须计算)再次感谢您的回答

以上是关于在 django orm 中聚合多对多的主要内容,如果未能解决你的问题,请参考以下文章

Django,在 self 类中的多对多关系中,我如何在 ORM 方面相互引用?

Django中ORM多对多表的操作

Django聚合查询 orm字段及属性

15)django-ORM(多对多)

Django ORM - 通过模型查询多对多?

ORM Django 多对多