агрегировать многие ко многим в django orm

#python #django #django-models #django-rest-framework #django-orm

#python #django #django-модели #django-rest-framework #django-orm

Вопрос:

я хочу создать отчет о сумме продолжительности, которую консультант консультирует в этом месяце. моя модель :

 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 раз с отдельным итогом.
когда я использую значения метода (‘id’) или использую distinct(), это не влияет на результат. моя БД — mysql и не может использовать distinct (‘id’).
мне нужен сериализатор запросов для передачи, пожалуйста, помогите мне исправить этот запрос и
есть ли какой-либо способ использовать сериализаторы django-rest-framework для этого набора запросов?

Ответ №1:

Используя a Case When , для каждой 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))
) 

Комментарии:

1. спасибо за ответ, как я могу вычислить продолжительность уведомления об этом в этом месяце. это означает, что это может начаться в прошлом месяце (продолжительность этого месяца должна быть вычислена), а его начало и конец в этом месяце, а также начаться в этом месяце и закончиться в следующем месяце (продолжительность этого месяца должна быть вычислена) еще раз спасибо за ваш ответ