✅ Разбор ответов на самопроверку

🎯 DRF-блок: Уроки 33–38 К оглавлению урока

⚡ Ключевые ответы

  • get_queryset() позволяет фильтровать динамически; queryset — статичный набор.
  • ModelViewSet — когда нужен полный CRUD; Generic Views — когда нужно ограничить операции.
  • DefaultRouter добавляет корневую страницу API; SimpleRouter — нет.
  • SoftDeleteManager автоматически исключает помеченные записи из всех запросов.
  • CursorPagination — для часто изменяемых данных; PageNumberPagination — для стабильных.

Блок A: GenericAPIView и Generic Views

Ответ 1: GenericAPIView

GenericAPIView расширяет APIView, добавляя встроенную поддержку queryset и serializer_class, а также методы для работы с данными (get_queryset, get_object, get_serializer). Он является основой для Generic Views и ViewSets, но сам по себе не реализует никаких HTTP-методов.

APIView — базовый класс с методами get/post/put/patch/delete, которые нужно реализовывать вручную. GenericAPIView добавляет шаблонные инструменты для работы с моделями.

Ответ 2: queryset и serializer_class

queryset — определяет набор данных, с которым работает представление. serializer_class — указывает класс сериализатора для преобразования данных. Оба атрибута можно переопределить через методы get_queryset() и get_serializer_class() для динамического поведения.

Ответ 3: Динамический queryset

Метод get_queryset(). Он вызывается при каждом запросе, поэтому в нём можно использовать параметры запроса (self.request.query_params), данные пользователя (self.request.user) и другие динамические данные.

def get_queryset(self):
    queryset = Book.objects.all()
    author = self.request.query_params.get('author')
    if author:
        queryset = queryset.filter(author=author)
    return queryset

Ответ 4: Все 9 Generic Views

КлассМетодыНазначение
ListAPIViewGETСписок объектов
CreateAPIViewPOSTСоздание
RetrieveAPIViewGETОдин объект
UpdateAPIViewPUT/PATCHОбновление
DestroyAPIViewDELETEУдаление
ListCreateAPIViewGET + POSTСписок + создание
RetrieveUpdateAPIViewGET + PUT/PATCHЧтение + обновление
RetrieveDestroyAPIViewGET + DELETEЧтение + удаление
RetrieveUpdateDestroyAPIViewGET + PUT/PATCH + DELETEПолный CRUD для одного объекта

Ответ 5: lookup_field и lookup_url_kwarg

lookup_field указывает поле модели для поиска объекта (по умолчанию 'pk'). lookup_url_kwarg указывает имя параметра URL, из которого берётся значение.

class GenreDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Genre.objects.all()
    serializer_class = GenreSerializer
    lookup_field = 'name'             # ищем по полю name
    lookup_url_kwarg = 'genre_name'   # из URL <str:genre_name>

# GET /genres/comedy/ → ищет Genre.objects.get(name='comedy')

Ответ 6: get_object()

Используется для получения одного экземпляра модели. Применяет lookup_field и lookup_url_kwarg, автоматически вызывает check_object_permissions() и бросает Http404 при отсутствии. Можно переопределить для добавления дополнительных условий фильтрации (например, исключения заблокированных записей).

Ответ 7: get_serializer_context()

Переопределить метод get_serializer_context() и добавить нужные данные в словарь контекста. Сериализатор получает контекст через self.context.

def get_serializer_context(self):
    context = super().get_serializer_context()
    context['include_related'] = True
    return context

# В сериализаторе:
def to_representation(self, instance):
    rep = super().to_representation(instance)
    if self.context.get('include_related'):
        rep['extra'] = ...
    return rep

Блок B: ViewSets и Router

Ответ 8: ViewSets

ViewSet — класс, объединяющий логику нескольких HTTP-методов (list, create, retrieve, update, destroy) в одном классе. Главное преимущество — автоматическая маршрутизация через Router и меньше дублирования кода. Вместо 2 отдельных Generic Views достаточно одного ViewSet.

Ответ 9: Три вида ViewSets

  • ModelViewSet — полный CRUD; когда нужны все операции.
  • ReadOnlyModelViewSet — только list и retrieve; для read-only API.
  • GenericViewSet — базовый класс, набор операций задаётся вручную через миксины; для кастомных комбинаций.

Ответ 10: Кастомная логика перед сохранением

Переопределить метод perform_create(self, serializer) или create(self, request, *args, **kwargs). Первый вызывается внутри стандартного create и подходит для добавления данных при сохранении (serializer.save(owner=request.user)). Второй даёт полный контроль над ответом.

Ответ 11: @action

Декоратор @action добавляет кастомный метод к ViewSet. Параметры:

  • detail=True → URL: /items/{pk}/action/
  • detail=False → URL: /items/action/
  • methods=['get', 'post'] — список допустимых HTTP-методов
  • url_path='custom-name' — переопределяет суффикс URL

Ответ 12: DefaultRouter vs SimpleRouter

DefaultRouter добавляет корневую страницу /api/, которая показывает список всех зарегистрированных маршрутов в формате JSON. SimpleRouter генерирует те же маршруты для CRUD, но без корневой страницы. DefaultRouter удобнее при разработке.

Блок C: Фильтрация, пагинация, логирование

Ответ 13: filter_backends

Атрибут filter_backends определяет, какие механизмы фильтрации применяются к queryset. Три стандартных:

  • DjangoFilterBackend — фильтрация по точным полям (filterset_fields)
  • SearchFilter — текстовый поиск (search_fields)
  • OrderingFilter — сортировка (ordering_fields)

Ответ 14: Параметры запроса filter backends

  • DjangoFilterBackend: ?author=Fitzgerald (по имени поля)
  • SearchFilter: ?search=Gatsby
  • OrderingFilter: ?ordering=price (ASC), ?ordering=-price (DESC)

Ответ 15: Три класса пагинации

  • PageNumberPagination — навигация по номеру страницы: ?page=2. Прост, но при вставках данных страницы могут смещаться.
  • LimitOffsetPagination — задаётся ?limit=5&offset=10. Гибко, привычно для SQL-разработчиков.
  • CursorPagination — использует непрозрачный курсор. Стабильна при частых изменениях данных, но нельзя перейти на произвольную страницу.

Ответ 16: Глобальная пагинация

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,
}
# Применяется ко всем представлениям, кроме тех, где явно указан pagination_class

Ответ 17: Логирование SQL

# settings.py
LOGGING = {
    'version': 1,
    'handlers': {
        'console': {'level': 'DEBUG', 'class': 'logging.StreamHandler'},
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'level': 'DEBUG',
        },
    },
}
# Требует DEBUG = True

Блок D: Soft Deletion, ленивая загрузка, транзакции

Ответ 18: Soft Deletion

Мягкое удаление — записи не удаляются физически, а помечаются флагом is_deleted=True. Шаги реализации:

  1. Добавить поля is_deleted и deleted_at в модель.
  2. Переопределить метод delete() модели — вместо физического удаления установить флаги.
  3. Создать SoftDeleteManager с фильтром is_deleted=False.

Ответ 19: Менеджер модели

Менеджер модели — класс, управляющий запросами (Model.objects). SoftDeleteManager нужен, чтобы все запросы (.all(), .filter(), .get()) автоматически исключали записи с is_deleted=True, без необходимости добавлять этот фильтр вручную везде.

Ответ 20: Ленивая загрузка и N+1

Ленивая загрузка — QuerySet не выполняет SQL до момента обращения к данным. Это позволяет цеплять .filter(), .order_by() и т.д. без лишних запросов.

Проблема N+1: при итерации по объектам с доступом к связанным данным каждый объект вызывает отдельный запрос.

Решения: select_related() для FK/OneToOne; prefetch_related() для ManyToMany и обратных FK.

Ответ 21: select_related vs prefetch_related

  • select_related() — выполняет SQL JOIN, загружает связанные объекты в одном запросе. Применяется для ForeignKey и OneToOneField.
  • prefetch_related() — выполняет отдельный запрос для связанных объектов и объединяет в Python. Применяется для ManyToManyField и обратных ForeignKey (related_name).

Ответ 22: Транзакции и API

Транзакция — группа операций, выполняемых как единое целое (атомарность, изолированность, консистентность, долговечность — ACID).

  • transaction.atomic — создаёт атомарный блок (декоратор или контекстный менеджер)
  • transaction.on_commit — действие после успешного коммита
  • transaction.set_rollback — явный откат транзакции в конце блока

Ответ 23: @transaction.atomic vs with transaction.atomic()

Оба обеспечивают атомарность, но:

  • @transaction.atomic — декоратор на всю функцию. Если функция успешно завершается, транзакция фиксируется; при исключении — откатывается.
  • with transaction.atomic(): — контекстный менеджер для части кода внутри функции. Можно вложить несколько блоков; вложенные создают точки сохранения (savepoints).