Django
Django — фреймворк «со всем в комплекте», и главная его особенность не размер, а то, что каждый слой прячет реальную работу за обычным на вид Python-кодом. Book.objects.filter(...) выглядит как готовый список, но это QuerySet, который ещё ни разу не сходил в базу. class Meta выглядит как вложенный класс, но это конфигурация, которую читает метакласс в момент построения модели. @receiver(post_save) выглядит как подписка на событие в фоне, но получатель выполняется синхронно внутри вашего запроса и задерживает ответ. Почти каждая ошибка на собеседовании — это удивление от того, что скрытая машинерия сработала не так, как подсказывала внешняя форма.
Отсюда понятно, что именно проверяют. Не знание имён методов — их можно посмотреть, — а умение назвать момент, в который что-то происходит, и число запросов, которые при этом уходят в базу. Когда выполняется SQL. Сколько запросов сделает цикл по book.author. Что происходит между чтением поля в Python и его записью обратно. В каком порядке DRF запускает валидацию и почему validate(attrs) иногда не вызывается вовсе. Слои ниже разбирают эти механизмы по одному — от философии фреймворка к устройству ORM и дальше к циклу запроса.
Карта темы
- Django и Flask — две философии — что значит «со всем в комплекте», что Flask оставляет вам и как выбирать между ними по задаче, а не по вкусу.
- ORM и ленивый QuerySet — модель как описание таблицы, а
QuerySetкак отложенный запрос; точный список моментов, в которые SQL наконец выполняется. - Внутренний класс Meta — вложенная конфигурация, которую метакласс читает при построении класса, и почему поля туда не кладут.
- Связи и связующая таблица —
ForeignKey,OneToOneFieldиManyToManyFieldна уровне столбцов и таблиц, плюсthrough-модель. - Абстрактная база против наследования таблиц —
abstract = Trueне создаёт таблицу и раздаёт столбцы; конкретное наследование создаёт таблицу и скрытыйJOIN. - N+1, select_related и prefetch_related — откуда берётся сотня запросов, что делает
JOIN, а что второй запрос со сборкой в Python. - Выражения F, Q и Case — как перенести вычисление в SQL, зачем это нужно против гонки read-modify-write и как собрать произвольный порядок.
- Middleware и порядок цепочки — вложенные обёртки вокруг view, фазы запроса и ответа, короткое замыкание и цена неверного порядка.
- Аутентификация и разрешения — сессия на cookie, ленивый
request.user, хэширование паролей, группы и права. - CSRF — токен и небезопасные методы — почему GET освобождён, что реально сверяется и чем
@csrf_exemptопасен. - Сигналы и неявный поток управления — синхронная доставка, операции, которые сигналов не шлют, и почему явный вызов почти всегда лучше.
- Сериализатор DRF и порядок валидации — четыре шага проверки данных и точное место, где живёт кросс-полевое правило.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
Считать, что Book.objects.filter(...) уже сходил в базу | QuerySet ленив — SQL уходит только на итерации, list(), len(), bool(), индексации или repr(); лишний вызов рядом с циклом даёт второй запрос вместо переиспользования кэша |
Применять select_related к ManyToManyField или обратному ForeignKey | FieldError — один JOIN умеет дотянуть только одиночную связь; для множественных нужен prefetch_related |
| Прочитать поле в Python, прибавить и сохранить обратно | Между чтением и записью успевает вклиниться другой процесс, и один из инкрементов теряется; F('views') + 1 переносит арифметику в SQL и снимает гонку |
Выносить общие поля в обычную модель вместо abstract = True | Появляется лишняя таблица и неявный OneToOneField, и каждый доступ к унаследованному полю начинает стоить JOIN |
Ждать post_save от queryset.update() или bulk_create() | Эти операции идут одним SQL мимо Model.save(), поэтому получатели молчат и «надёжная» логика в сигнале просто не выполняется |
Вешать @csrf_exempt, чтобы убрать 403 | Проверка снята с view целиком, и форму теперь может отправить любой сторонний сайт; настоящая причина почти всегда — незаполненный заголовок X-CSRFToken |
Ставить AuthenticationMiddleware выше SessionMiddleware | ImproperlyConfigured на первом же запросе — порядок в MIDDLEWARE задаёт вложенность обёрток, а не просто перечисляет настройки |
Класть кросс-полевую проверку в validate_<field> сериализатора | Метод видит только своё поле; сравнение двух полей живёт в validate(attrs), который вообще не запустится, если хоть одно поле не прошло проверку |
Значение для собеседований
Django спрашивают у middle-кандидатов почти всегда, и разговор быстро съезжает с фреймворка на базу данных. Начинают с простого — «что такое ORM», «что такое middleware», «что такое сигналы», — но настоящая проверка идёт следом, вопросом «а сколько запросов сделает вот этот цикл». Здесь и выясняется, писали ли вы прод — человек, который хоть раз ловил N+1 в логах, отвечает про select_related и prefetch_related не определениями, а разницей между JOIN и вторым запросом со сборкой в Python. Второй такой же индикатор — задача про инкремент счётчика — F() в ответе означает, что кандидат видит разницу между вычислением в Python и вычислением в SQL.
Дальше идут вопросы на аккуратность. Про Meta проверяют, не путаете ли вы конфигурацию с полями и с базовым классом. Про CSRF просят объяснить, почему GET освобождён — правильный ответ про отсутствие побочных эффектов по семантике HTTP, а не про скорость. У вопроса про сигналы почти всегда есть скрытая вторая часть — синхронны ли они и стоит ли ими пользоваться; ответ «синхронны, и в большинстве случаев лучше явный вызов» ценится выше перечисления всех встроенных сигналов. Типичный провал везде один и тот же — кандидат пересказывает документацию верно, но не может назвать момент выполнения и цену в запросах.