Как правильно составить запрос к базе данных с помощью Django ORM?

Я застрял в том, как написать запрос, чтобы избежать циклов for

Расчет количества больничных дней за период первого создания больничного объекта для сотрудника + 365 дней (То есть за период года после первого создания объекта) По истечении года счетчик должен обнулиться и после итерации будет идти от первого создания объекта после даты (первый больничный объекта + 365 дней)

У меня есть две модели

class Employee(Model):
    birth_name = CharField(_('Nom De Naissance'), max_length=120)
    last_name = CharField(max_length=120, verbose_name="Nom D'Usage")
    first_name = CharField(_('Prénom'), max_length=120, unique=True)


class Sick(Model):
    class TypeSick(Enum):
        Initial = 'Initial'
        Prolongation = 'Prolongation'
        Accident_Travail = 'Accident Travail'
        Accident_Trajet = 'Accident Trajet'
        Congés_Maternités = 'Congés Maternités'
        AUTRES = 'Autres'

    type_sick = CharField(
        max_length=120,
        choices=choice_handler(TypeSick),
    )

    start_date = DateField(
        editable=True, null=False, blank=False
    )
    end_date = DateField(
        editable=True, null=True, blank=True
    )

    after_year = DateField(editable=True, null=True, blank=True)

    employee = ForeignKey(
        Employee,
        on_delete=SET_NULL,
        null=True,
        blank=True,
        verbose_name='Employe',
        related_name='sick',
    )

Вот что я сделал, чтобы получить первый больничный каждого сотрудника и подсчитать его конец года +365 дней

def save_sick_end_year(
    request: HttpRequest,
    sick_model: Model,
    type_sick: str,
    employee_model: Model,
) -> tuple[
    QuerySet[Model, dict[str, dt.date]],
    QuerySet[Model, dict[str, dt.date]],
]:
    employees = employee_model.objects.all()

    sick_subquery = sick_model.objects.filter(
        employee=OuterRef('pk'), type_sick=type_sick
    ).order_by('start_date')

    _sick = employees.annotate(
        _start=Subquery(sick_subquery.values('start_date')[:1]),
        _end=ExpressionWrapper(
            F('_start') + dt.timedelta(days=365).days,
            output_field=DateField(),
        ),
    ).values_list('first_name', 'last_name', '_start', '_end')

    return _sick

выход: <QuerySet [('AAA', 'BBB', datetime.date(2024, 5, 5), datetime.date(2025, 5, 5)), ('CCC', 'XXX', datetime.date(2024, 6, 6), datetime.date(2025, 6, 6))]>

Теперь задача найти все больные объекты между этими двумя датами _start и _end

Вот мое модифицированное решение для расчета всех больничных дней за определенный период

    sick_subquery = (
        sick_model.objects.filter(employee=OuterRef('pk'), type_sick=type_sick)
        .annotate(_date=Coalesce('employee__is_year', 'start_date'))
        .order_by(
            Case(
                When(
                    employee__is_year__isnull=False,
                    then=F('employee__is_year'),
                ),
                default=F('start_date'),
            )
        )
        .values('_date')[:1]
    )

    employees = (
        employee_model.objects.annotate(start=Subquery(sick_subquery))
        .values(
            'start',
        )
        .annotate(
            end=ExpressionWrapper(
                F('start') + dt.timedelta(days=365),
                output_field=DateField(),
            )
        )
        .values('start', 'end')
        .annotate(
            total_days=Sum(
                (
                    (F('sick__end_date') - F('sick__start_date'))
                    + dt.timedelta(days=1)
                ),
                output_field=DurationField(),
                filter=Q(
                    sick__start_date__gte=F('start'),
                    sick__end_date__lte=F('end'),
                ),
            )
        )
        .annotate(total_days=Coalesce(Extract('total_days', 'day'), Value(0)))
        .values(
            'uuid',
            'first_name',
            'last_name',
            'is_year',
            'start',
            'end',
            'total_days',
        )
        .order_by('-total_days')
    )
Вернуться на верх