Написание выражения 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()),
    ),
)
Вернуться на верх