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
? Что я здесь упустил?