Написание выражения Django Func() с несколькими параметрами и указание порядка их следования
Я использую Func() Expressions, чтобы использовать этот ответ и вычислить разницу между двумя датами в рабочих днях:
class BusinessDaysBetween(Func):
"""Implementation of a Postgres function to compute the working holidays between two fields."""
template = """
(SELECT COUNT(*) FROM generate_series(%(expressions)s, interval '1 day') s(day)
WHERE EXTRACT(DOW FROM s.day) NOT IN (0, 6))
"""
arity = 2
output_field = IntegerField()
Однако у меня возникли проблемы с форматированием Date и DateTime. Поэтому я хочу вызвать функции, упомянутые в этом ответе.
Мой отредактированный код выглядел следующим образом:
class BusinessDaysBetween(Func):
"""Implementation of a Postgres function to compute the working holidays between two fields."""
template = """
(
SELECT COUNT(*) FROM generate_series(
TO_CHAR(CAST(%(expressions)s AS DATE), 'YYYY-MM-DD'),
TO_CHAR(CAST(%(expressions)s[1] AS DATE), 'YYYY-MM-DD'),
interval '1 day'
) s(day)
WHERE EXTRACT(DOW FROM s.day) NOT IN (0, 6)
)
"""
arity = 2
output_field = IntegerField()
Проблема в том, что я ставлю оба параметра на первое место, Я не знаю, как указать порядок появления параметров. Я уже пробовал:
- With
{0}
и{1}
и пишет, что произошла синтаксическая ошибка. %(expressions)s
и%(expressions)s[1]
и ничего.- При
%s
возникает ошибка «недостаточно аргументов для строки формата».
На самом деле, ваш первый вариант функции BusinessDaysBetween
работает, вам просто нужно сделать преобразование типа datetime
в date
с помощью functions.Cast
. Например, будут работать такие запросы к базе данных:
import datetime
from django.db import models
from django.db.models.functions import Cast
class BusinessDaysBetween(models.Func):
"""Implementation of a Postgres function to compute the working holidays between two fields."""
template = """
(SELECT COUNT(*) FROM generate_series(%(expressions)s, interval '1 day') s(day)
WHERE EXTRACT(DOW FROM s.day) NOT IN (0, 6))
"""
arity = 2
output_field = models.IntegerField()
end_at = datetime.datetime.now(datetime.UTC)
queryset1 = YourModel.objects.annotate(
business_days_count=BusinessDaysBetween(
Cast(end_at - datetime.timedelta(days=10), models.DateField()),
Cast(end_at, models.DateField()),
),
)
# or using `models.F` to reference your model field
queryset2 = YourModel.objects.annotate(
business_days_count=BusinessDaysBetween(
Cast(models.F('started_at'), models.DateField()),
Cast(models.F('finished_at'), models.DateField()),
),
)