Эта идея похожа на "гибридные атрибуты" из SQLAlchemy , о которых спрашивали раньше - я не был полностью удовлетворен ни одним из ответов из этой цепочки потоков ( такие вещи, как сохранение этого вычисленного значения в дополнительном поле таблицы и постоянное его обновление).
У вас может быть какая-то внутренняя функция, которую используют ваше свойство и функция ExpressionWrapper, при условии, что требуемые операторы перегружены, чтобы принимать либо действительные значения, либо объекты F()
(например, основные математические операторы).
def multiplication(x, y):
return x * y # trivial here but it could be any mathematical expression really
def _hist_price(orderable_field):
return ExpressionWrapper(
multiplication(
F(f"{orderable_field}__hist_unit_price"),
F(f"{orderable_field}__quantity")
),
output_field=DecimalField()
)
@property
def hist_price(self):
return multiplication(self.hist_unit_price, self.quantity)
Если в одной из этих гибридных функций она усложняется, чем базовые числовые операции, и вы хотите избежать дублирования бизнес-логики, вам нужно написать функцию-обертку, которая может преобразовываться в правильный вывод с использованием функции python для вызывающего свойства и функция, которая может работать с F-объектами для вызывающей стороны набора запросов, чтобы поддерживать перегрузку оператора. Но это приведет к коду, который анализирует аргументы, чтобы решить, что делать, что может быть не интуитивно понятно, так что это действительно компромисс в любом случае.
В грубом псевдокоде одна из этих пользовательских функций будет выглядеть как
def _hybrid_lower(value):
if isinstance(value, F): # maybe F would be sufficient or some other class higher in the hierarchy
# https://docs.djangoproject.com/en/2.2/ref/models/expressions/#func-expressions
return Func(value, function='LOWER')
else:
return value.lower()
и затем вы можете использовать эту пользовательскую функцию в своей функции, которую вызывают и свойство, и набор запросов. Некоторое дублирование кода может оказаться не самым худшим компромиссом, если вам действительно понадобятся действительно сложные функции, такие как операции с базами данных и Python.