Класс ExpressionWrapper
Класс ExpressionWrapper применяется для обёртки выражений
(например, F-выражений или агрегаций) с явным указанием
типа выходных данных. Это особенно важно, когда выражение
используется в условиях фильтрации или сортировки, и Django
не может автоматически определить тип результата. В качестве
основного аргумента передаётся выражение, а параметр
output_field определяет тип поля результата.
Синтаксис
from django.db.models import ExpressionWrapper, F
from django.db.models import IntegerField
ExpressionWrapper(F('field') + 1, output_field=IntegerField())
Пример . Модель
Рассмотрим модель Product с полями price и discount:
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
discount = models.IntegerField(default=0) # percent
def __str__(self):
return self.name
Пример . С вычислением цены
Вычислим итоговую цену с учётом скидки для всех товаров:
from django.db.models import ExpressionWrapper, F, DecimalField
from myapp.models import Product
# Calculate final price with discount
products = Product.objects.annotate(
final_price=ExpressionWrapper(
F('price') * (1 - F('discount') / 100.0),
output_field=DecimalField(max_digits=10, decimal_places=2)
)
)
for product in products:
print(f"{product.name}: {product.final_price}")
Результат выполнения кода:
"Laptop: 850.50"
"Phone: 560.00"
"Tablet: 320.00"
Пример . С условием фильтрации
Используем ExpressionWrapper для фильтрации товаров, у которых итоговая цена меньше заданного значения:
from django.db.models import ExpressionWrapper, F, DecimalField, Q
from myapp.models import Product
# Filter products where final price < 500
discounted_products = Product.objects.filter(
ExpressionWrapper(
F('price') * (1 - F('discount') / 100.0),
output_field=DecimalField(max_digits=10, decimal_places=2)
) < 500
)
for product in discounted_products:
print(f"{product.name}: {product.price}")
Результат выполнения кода:
"Phone: 700.00"
"Tablet: 400.00"
Пример . С использованием в сортировке
Отсортируем товары по итоговой цене с учётом скидки:
from django.db.models import ExpressionWrapper, F, DecimalField
from myapp.models import Product
# Order by final price
products = Product.objects.annotate(
final_price=ExpressionWrapper(
F('price') * (1 - F('discount') / 100.0),
output_field=DecimalField(max_digits=10, decimal_places=2)
)
).order_by('final_price')
for product in products:
print(f"{product.name}: {product.final_price}")
Результат выполнения кода:
"Tablet: 320.00"
"Phone: 560.00"
"Laptop: 850.50"
Пример . С агрегацией
Вычислим среднюю итоговую цену товаров с использованием агрегации:
from django.db.models import ExpressionWrapper, F, DecimalField, Avg
from myapp.models import Product
# Calculate average final price
avg_price = Product.objects.aggregate(
avg_final=Avg(
ExpressionWrapper(
F('price') * (1 - F('discount') / 100.0),
output_field=DecimalField(max_digits=10, decimal_places=2)
)
)
)
print(avg_price['avg_final'])
Результат выполнения кода:
"576.83"