🔖 Справочник — Урок 36

← К оглавлению урока

⚡ Быстрый справочник

  • is_deleted = models.BooleanField(default=False) — поле мягкого удаления
  • deleted_at = models.DateTimeField(null=True, blank=True) — время удаления
  • objects = SoftDeleteManager() — подключить кастомный менеджер
  • Book.objects.select_related('publisher') — JOIN для FK
  • Book.objects.prefetch_related('genres') — 2 запроса для M2M
  • @transaction.atomic — атомарный декоратор
  • with transaction.atomic(): — атомарный блок
  • transaction.on_commit(callback) — после коммита

Шаблон: Soft Deletion

managers.py

from django.db import models

class SoftDeleteManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(is_deleted=False)

models.py — полная интеграция

from django.db import models
from django.utils import timezone
from .managers import SoftDeleteManager

class MyModel(models.Model):
    # ... остальные поля ...
    is_deleted = models.BooleanField(default=False)
    deleted_at = models.DateTimeField(null=True, blank=True)

    objects = SoftDeleteManager()       # только не удалённые
    all_objects = models.Manager()      # все записи (для администрирования)

    def delete(self, *args, **kwargs):
        self.is_deleted = True
        self.deleted_at = timezone.now()
        self.save()

    def restore(self):
        """Восстановить мягко удалённую запись."""
        self.is_deleted = False
        self.deleted_at = None
        self.save()

    class Meta:
        abstract = True  # если используется как миксин

Шаблон: QuerySet-оптимизация

select_related — для ForeignKey и OneToOneField

# Один связанный объект (FK)
qs = Book.objects.select_related('publisher')

# Цепочка связей
qs = Book.objects.select_related('publisher__country')

prefetch_related — для ManyToMany и обратных FK

# ManyToMany
qs = Book.objects.prefetch_related('genres')

# Обратный FK (все книги издателя)
qs = Publisher.objects.prefetch_related('book_set')

# Комбинация
qs = Book.objects.select_related('publisher').prefetch_related('genres')

Шаблон: Транзакции

Декоратор @transaction.atomic

from django.db import transaction

@transaction.atomic
@api_view(['POST'])
def my_view(request):
    # Вся функция — одна транзакция
    obj1 = Model1.objects.create(...)
    obj2 = Model2.objects.create(...)
    return Response(...)

Контекстный менеджер with transaction.atomic()

@api_view(['POST'])
def my_view(request):
    with transaction.atomic():
        obj1 = Model1.objects.create(...)
        obj2 = Model2.objects.create(...)
    # Код вне блока выполняется после коммита
    return Response(...)

transaction.on_commit()

with transaction.atomic():
    book = Book.objects.create(...)
    # Отправить уведомление только после успешного коммита
    transaction.on_commit(lambda: send_notification(book.id))

transaction.set_rollback()

with transaction.atomic():
    book = Book.objects.create(...)
    if some_condition_fails:
        transaction.set_rollback(True)  # пометить для отката

Ручное управление (редко)

transaction.set_autocommit(False)
try:
    # операции
    transaction.commit()
except Exception:
    transaction.rollback()
finally:
    transaction.set_autocommit(True)

Таблица: транзакционный API Django

Функция / декораторНазначение
transaction.atomicДекоратор или контекстный менеджер: атомарный блок
transaction.on_commit(fn)Вызвать fn после успешного COMMIT
transaction.set_rollback(True)Пометить транзакцию для отката в конце блока
transaction.set_autocommit(False)Отключить автокоммит (ручной режим)
transaction.commit()Явный COMMIT (только в ручном режиме)
transaction.rollback()Явный ROLLBACK (только в ручном режиме)

Миграции для новых полей

# После добавления is_deleted / deleted_at в модель
python manage.py makemigrations
python manage.py migrate