Django фильтр с использованием условных выражений

У меня есть модель (допустим, класс Foo), которая содержит 2 важных атрибута, и каждый атрибут может иметь одно из два значения, вот некоторый псевдокод:

period_condition = cons.BEFORE or cons.AFTER 
period_days = const.START_DATE or cons.END_DATE

Теперь мне нужно построить фильтр динамически на основе этих двух значений. Я могу сделать это с помощью if-else, но это выглядит довольно уродливо и трудно читается/следует:

def list_subscriptions_for_sending(foo: Foo) -> QuerySet[Subscription]:
    days = foo.days

    if foo.period_condition == BEFORE:
        if foo.period_date == START_DATE:
            filter = Q(start_date=get_today() + timedelta(days=days))
        if foo.period_date == END_DATE:
            filter = Q(end_date=get_today() + timedelta(days=days))

    if foo.period_condition == AFTER:
        if foo.period_date == START_DATE:
            filter = Q(start_date=get_today() - timedelta(days=days))
        if foo.period_date == END_DATE:
            filter = Q(end_date=get_today() - timedelta(days=days))

Я пытался использовать Case и When - then условные выражения Django, но получил следующую ошибку:

*** TypeError: Cannot filter against a non-conditional expression.

Я не могу понять, как это выражение является безусловным. В моем понимании, оно проходит через каждый When из Case и если выражение истинно, оно присваивает выражение Q из then :

filter = Case(
        When(
            Q(
                foo.period_condition == BEFORE
            )
            & Q(
                foo.period_date == START_DATE
            ),
            then=Q(start_date=get_today() + timedelta(days=days)),
        ),
        When(
            Q(
                foo.period_condition == BEFORE
            )
            & Q(
                foo.period_date == END_DATE
            ),
            then=Q(end_date=get_today() + timedelta(days=days)),
        ),
        When(
            Q(
                foo.period_condition == AFTER
            )
            & Q(
                foo.period_date == START_DATE
            ),
            then=Q(start_date=get_today() - timedelta(days=days)),
        ),
        When(
            Q(
                foo.period_condition == AFTER
            )
            & Q(
                foo.period_date == END_DATE
            ),
            then=Q(end_date=get_today() - timedelta(days=days)),
        ),
        default=Q()
    )

subscriptions = Subscription.objects.filter(filter)

Если я печатаю переменную filter в отладчике, это дает мне именно то, что я ожидал:

ipdb> filter
<Case: CASE 
WHEN <Q: (AND: (AND: False, False))> THEN <Q: (AND: ('start_date', datetime.date(2022, 9, 3)))>, 
WHEN <Q: (AND: (AND: False, True))> THEN <Q: (AND: ('end_date', datetime.date(2022, 9, 3)))>, 
WHEN <Q: (AND: (AND: True, False))> THEN <Q: (AND: ('start_date', datetime.date(2022, 8, 14)))>, 
WHEN <Q: (AND: (AND: True, True))> THEN <Q: (AND: ('end_date', datetime.date(2022, 8, 14)))>, # <=== This
ELSE <Q: (AND: )>>  

Правильно ли я понимаю выражение When-then? Что я здесь упустил?

Вернуться на верх