Класс Case
Класс Case применяется в сочетании с When для создания условных выражений в запросах Django ORM. Он позволяет выполнять условную логику прямо на уровне базы данных, определяя значения полей в зависимости от выполнения определенных условий. Это особенно полезно, когда вам нужно вычислить значение поля на основе логических условий без выполнения дополнительных запросов или обработки в Python. Класс Case принимает один или несколько объектов When, которые определяют условия и соответствующие им результаты, а также необязательный аргумент default для значения по умолчанию.
Синтаксис
from django.db.models import Case, When, Value
Case(
When(condition1, then=value1),
When(condition2, then=value2),
default=default_value,
output_field=output_field
)
Пример
Рассмотрим простой пример с моделью Product, где нужно добавить текстовое описание уровня цен в зависимости от значения поля price:
from django.db import models
from django.db.models import Case, When, Value, CharField
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.IntegerField()
# Добавляем аннотацию с категорией цены
products = Product.objects.annotate(
price_category=Case(
When(price__lt=100, then=Value("Cheap")),
When(price__gte=100, price__lt=500, then=Value("Medium")),
When(price__gte=500, then=Value("Expensive")),
default=Value("Unknown"),
output_field=CharField()
)
)
for product in products:
print(f"{product.name}: {product.price_category}")
Результат выполнения кода:
"Phone: Medium"
"Book: Cheap"
"Laptop: Expensive"
Пример
Теперь рассмотрим более сложный пример с использованием нескольких условий и агрегации. Допустим, у нас есть модель Order с полем status, и мы хотим подсчитать общее количество заказов в каждой категории статуса, а также создать аннотацию с текстовым описанием:
from django.db import models
from django.db.models import Case, When, Value, Count, IntegerField
class Order(models.Model):
STATUS_CHOICES = (
("pending", "Pending"),
("processing", "Processing"),
("completed", "Completed"),
)
status = models.CharField(max_length=20, choices=STATUS_CHOICES)
amount = models.IntegerField()
# Подсчет заказов с текстовым описанием статуса
orders = Order.objects.annotate(
status_description=Case(
When(status="pending", then=Value("Waiting for approval")),
When(status="processing", then=Value("Being processed")),
When(status="completed", then=Value("Delivered")),
default=Value("Unknown status"),
output_field=models.CharField()
)
).values("status_description").annotate(
total_orders=Count("id")
)
for order in orders:
print(f"{order['status_description']}: {order['total_orders']}")
Результат выполнения кода:
"Waiting for approval: 5"
"Being processed: 12"
"Delivered: 8"
Пример
В этом примере мы используем Case совместно с F выражением для обновления полей на основе значений других полей. Создадим модель Person с полями age и age_group, и заполним второе поле на основе первого:
from django.db import models
from django.db.models import Case, When, Value, F, CharField
class Person(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
age_group = models.CharField(max_length=20, blank=True)
# Обновляем age_group для всех записей
Person.objects.update(
age_group=Case(
When(age__lt=18, then=Value("Child")),
When(age__gte=18, age__lt=60, then=Value("Adult")),
When(age__gte=60, then=Value("Senior")),
default=Value("Unknown"),
output_field=CharField()
)
)
# Выводим результаты
people = Person.objects.all()
for person in people:
print(f"{person.name} ({person.age}): {person.age_group}")
Результат выполнения кода:
"Alice (25): Adult"
"Bob (70): Senior"
"Charlie (12): Child"