Как правильно составить запрос к базе данных с помощью 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')
)