Класс Case

Класс Case применяется в сочетании с When для создания условных выражений в запросах Django ORM. Он позволяет выполнять условную логику прямо на уровне базы данных, определяя значения полей в зависимости от выполнения определенных условий. Это особенно полезно, когда вам нужно вычислить значение поля на основе логических условий без выполнения дополнительных запросов или обработки в Python. Класс Case принимает один или несколько объектов When, которые определяют условия и соответствующие им результаты, а также необязательный аргумент default для значения по умолчанию.

Синтаксис

pythonfrom 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:

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

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

python"Phone: Medium"
"Book: Cheap"
"Laptop: Expensive"

Пример

Теперь рассмотрим более сложный пример с использованием нескольких условий и агрегации. Допустим, у нас есть модель Order с полем status, и мы хотим подсчитать общее количество заказов в каждой категории статуса, а также создать аннотацию с текстовым описанием:

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

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

python"Waiting for approval: 5"
"Being processed: 12"
"Delivered: 8"

Пример

В этом примере мы используем Case совместно с F выражением для обновления полей на основе значений других полей. Создадим модель Person с полями age и age_group, и заполним второе поле на основе первого:

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

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

python"Alice (25): Adult"
"Bob (70): Senior"
"Charlie (12): Child"

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

  • класс When,
    который определяет условие и результат внутри Case
  • класс Value,
    который используется для явного указания значения в выражении
  • класс F,
    который позволяет ссылаться на поля модели в запросах
  • класс Count,
    который выполняет подсчет записей в запросе