Класс ExpressionWrapper

Класс ExpressionWrapper применяется для обёртки выражений (например, F-выражений или агрегаций) с явным указанием типа выходных данных. Это особенно важно, когда выражение используется в условиях фильтрации или сортировки, и Django не может автоматически определить тип результата. В качестве основного аргумента передаётся выражение, а параметр output_field определяет тип поля результата.

Синтаксис

pythonfrom django.db.models import ExpressionWrapper, F
from django.db.models import IntegerField

ExpressionWrapper(F('field') + 1, output_field=IntegerField())

Пример . Модель

Рассмотрим модель Product с полями price и discount:

pythonfrom 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

Пример . С вычислением цены

Вычислим итоговую цену с учётом скидки для всех товаров:

pythonfrom 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}")

Результат выполнения кода:

python"Laptop: 850.50"
"Phone: 560.00"
"Tablet: 320.00"

Пример . С условием фильтрации

Используем ExpressionWrapper для фильтрации товаров, у которых итоговая цена меньше заданного значения:

pythonfrom 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}")

Результат выполнения кода:

python"Phone: 700.00"
"Tablet: 400.00"

Пример . С использованием в сортировке

Отсортируем товары по итоговой цене с учётом скидки:

pythonfrom 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}")

Результат выполнения кода:

python"Tablet: 320.00"
"Phone: 560.00"
"Laptop: 850.50"

Пример . С агрегацией

Вычислим среднюю итоговую цену товаров с использованием агрегации:

pythonfrom 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'])

Результат выполнения кода:

python"576.83"

Смотрите также

  • класс F,
    который позволяет выполнять операции на уровне базы данных
  • класс Q,
    который используется для создания сложных условий фильтрации
  • класс Case,
    который реализует условные выражения в запросах
  • класс Value,
    который используется для обёртки значений в выражения