Django и DRF
Django всё ещё держит огромную долю Python-вакансий, и собес по нему крутится вокруг трёх узлов: как устроен MTV, что делает сериализатор DRF и почему QuerySet ленив. Последнее - источник самого частого продового бага, N+1, и его любят давать кодом.
Типовые формулировки: «чем select_related отличается от prefetch_related?», «когда QuerySet реально идёт в БД?», «ModelViewSet или APIView».
MTV и двусторонний сериализатор
Django это MTV (model-template-view): Model (данные, ORM), Template (представление), View (обработка запроса и логика). Роль «контроллера» из MVC (model-view-controller) по сути играет сам фреймворк - маршрутизация и диспетчеризация встроены.
DRF (Django REST Framework) Serializer - двусторонний мост: на входе валидирует и превращает JSON в объекты Python, на выходе сериализует модель в JSON. ModelViewSet собирает CRUD из модели плюс сериализатора (роутер строит URL), а APIView - ручной уровень с полным контролем, когда логика нестандартная.
// MIDDLEWARE - луковица: запрос идёт сверху вниз, ответ снизу вверх; Security и Authentication ставят раньше слоёв, что от них зависят.
- MTV
- Model-Template-View - Django-вариант MVC
- Serializer
- DRF: валидация и конвертация модель ↔ JSON
Ленивый QuerySet и N+1
QuerySet ленив: filter лишь строит запрос, а SQL уходит в БД только при итерации, len, list или срезе. Отсюда две вещи: удобный чейнинг фильтров, и случайный N+1, когда в цикле по объектам ты на каждой итерации дёргаешь связь, и это отдельный запрос на каждый объект.
Лечение зависит от типа связи. select_related делает JOIN для ForeignKey/OneToOne («многие к одному»); prefetch_related шлёт отдельный запрос и джойнит в Python для обратной FK (foreign key) и ManyToMany. Перепутал - N+1 останется недобитым: select_related на ManyToMany не сработает.
// get() на пустом результате бросает DoesNotExist, а не возвращает None; «объект или None» это filter().first().
for book in Book.objects.all(): # 1 запрос
print(book.author.name) # +N запросов -> N+1
Book.objects.select_related('author') # 1 JOIN -> починено- QuerySet
- ленивый набор строк; SQL выполняется при обращении к данным
- select/prefetch_related
- жадная загрузка связей против N+1
Как отвечать: «Чем select_related отличается от prefetch_related?»
Оба бьют N+1, но по-разному и для разных связей. select_related делает SQL JOIN и подтягивает связанное одним запросом - годится для ForeignKey и OneToOne, то есть отношения «многие к одному», где на объект приходится ровно одна связанная строка. prefetch_related шлёт отдельный второй запрос и склеивает результаты уже в Python это для обратных ForeignKey и ManyToMany, где на объект много связанных. Если перепутать и повесить select_related на ManyToMany, оно просто не сработает, и N+1 останется. Поэтому я смотрю на кардинальность связи и выбираю по ней.
Разведены и механика (JOIN против второго запроса), и область применения по кардинальности связи, назван конкретный провал при путанице - видно, что человек чинил N+1 руками, а не читал доку.
На чём валят
- −QuerySet в цикле по объектам: обращение к связи на каждой итерации даёт N+1 запросов.
- −select_related на ManyToMany не поможет - для «многих» нужен prefetch_related.
- −get() на пусто бросает DoesNotExist, а не возвращает None; «объект или None» это filter().first().
- −Забыть, что QuerySet ленив: тяжёлый запрос сработает не там, где написан filter, а где к нему обратятся.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 14, остальные разбираются в тренажёре.
- Что означает MTV в Django?A)Model — Template — View: данные, представление, логикаB)Method — Template — View: методы контроллера приложенияC)Model — Test — View: выделенный слой тестов в архитектуреD)Model — Transport — View: транспортный слой между ними
показать ответ и разбор
+A)Model — Template — View: данные, представление, логика// разбор: Django зовёт свою архитектуру MTV: Model — данные и ORM, Template — рендер представления, View — обработка запроса и логика. Это тот же MVC, но «контроллером» по сути выступает сам фреймворк (маршрутизация), а View играет роль обработчика-контроллера.
- Какова роль сериализатора в DRF?A)Кэшировать ответы вьюх между повторяющимися запросамиB)Валидировать вход и конвертировать модель в JSON и обратноC)Хранить схему миграций приложения между релизамиD)Маршрутизировать запросы к нужному обработчику по URL
показать ответ и разбор
+B)Валидировать вход и конвертировать модель в JSON и обратно// разбор: Serializer в DRF — двусторонний мост: на входе валидирует и превращает JSON в объекты Python и инстансы модели, на выходе сериализует модель в JSON. ModelSerializer выводит поля из модели. По сути аналог Pydantic-модели в экосистеме Django.
qs = User.objects.filter(active=True). Когда Django ORM пошлёт SQL?A)При первой итерации или материализации QuerySetB)Только при вызове .save() на полученном результатеC)При импорте модели, один раз на запуск процессаD)Сразу в строке с filter — запрос выполнится немедленнопоказать ответ и разбор
+A)При первой итерации или материализации QuerySet// разбор: QuerySet ленив: filter лишь строит запрос, SQL уходит при итерации, len(), list(), срезе или обращении к элементу. Это позволяет чейнить фильтры без лишних запросов. Обратная сторона — случайно дёрнуть QuerySet в цикле и получить N запросов.
- Чем ModelViewSet отличается от APIView в DRF?A)APIView сам собирает CRUD, а ViewSet требует расписать методы вручнуюB)APIView нужен исключительно для GraphQL-эндпоинтовC)ViewSet работает только на чтение, без операций записиD)ViewSet даёт готовый CRUD из модели и сериализатора
показать ответ и разбор
+D)ViewSet даёт готовый CRUD из модели и сериализатора// разбор: ModelViewSet собирает стандартный CRUD (list/create/retrieve/update/destroy) из модели и сериализатора, а роутер строит URL. APIView — ручной уровень: сам описываешь get/post. ViewSet экономит бойлерплейт на типовом REST, APIView даёт контроль на нестандартном.
- Важен ли порядок в списке MIDDLEWARE Django?A)Да: запрос идёт сверху вниз, ответ — снизу вверхB)Нет, Django сам сортирует middleware по важности задачC)Важен только для CSRF, остальные независимы друг от другаD)Порядок влияет лишь на скорость, но не на поведение
показать ответ и разбор
+A)Да: запрос идёт сверху вниз, ответ — снизу вверх// разбор: Middleware в Django — луковица: на входе запрос идёт списком сверху вниз, на выходе ответ — в обратном порядке. Поэтому Security/Authentication ставят раньше, чем то, что на них опирается. Перепутанный порядок ломает аутентификацию или пропускает проверки.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.