Какое обходное решение для ошибки Django/PostgreSQL GeneratedField
При определении GeneratedField в PostgreSQL нередко возникает следующая ошибка: django.db.utils.ProgrammingError: generation expression is not immutable
.
Эта ошибка упоминается в документах о специфическом для PostgreSQL ограничении:
...PostgreSQL требует, чтобы функции и операторы, на которые ссылается сгенерированный столбец, были помечены как IMMUTABLE.
Эта ошибка возникает почти во всех выражениях Func() (с указанной функцией базы данных). Например:
age_func = Func("date_of_birth", function="age")
age_years = Func(Value("year"), age_func, function="date_part", output_field=...)
Если в выражении GeneratedField используется age_years
, это приведет к вышеуказанной ошибке.
Другой воспроизводимый пример - функция Concat(). Если использовать ее как есть, Concat также будет вызывать ту же ошибку, но для нее существует обходной путь (эта пользовательская реализация может не понадобиться в Django 5.1).
Если бы я определял эти функции в sql, я полагаю, что не было бы проблемы пометить их как IMMUTABLE. Мой вопрос заключается в том, что, работая только с Django, как нам реализовать НЕЗАВИСИМЫЕ функции/выражения?
Я предполагаю, что существует способ передачи дополнительных аргументов/опций, которые будут использоваться для указания того, будет ли функция базы данных IMMUTABLE, STABLE, VOLATILE и т.д., но я понятия не имею, как это указать. Возможно, мы могли бы подклассифицировать Func и переопределить какой-нибудь метод, генерирующий sql, но опять же, я не знаю, кто за это отвечает.
Вы можете не использовать сгенерированный столбец, если выражение чувствительно ко времени. Действительно, если оно зависит, например, от NOW()
, или RANDOM()
, или от чего-либо, что не детерминировано, то это не работает, по крайней мере, пока. Но, вероятно, это все равно не имеет смысла: генерируемый столбец - это столбец, который вычисляется и хранится в таблице. Если возраст зависит от текущего времени, это означает, что каждый день записи должны обновляться, что сводит на нет всю цель генерируемого столбца.
Но здесь это, вероятно, также не пригодится: вы можете использовать .annotate(…)
[Django-doc] для определения возраста, когда это необходимо. Если вы хотите отфильтровать, вы можете использовать:
from datetime import date
Person.objects.filter(date_of_birth__range=(date(1994, 7, 16), date(1995, 7, 16)))
для людей, которым на данный момент 29 лет.