Как в Django ORM выбрать конкретное поле из связанной таблицы или загрузить связанные таблицы?
Что проверяют
Знаете ли вы, как Django превращает обращение к связанным моделям в SQL и как не устроить N+1. Интервьюер ждёт чёткой границы между select_related и prefetch_related и умения достать одно поле без загрузки целых объектов. Следом просят показать, как вы находите лишние запросы.
Как отвечать
Одно поле из связанной таблицы берётся через двойное подчёркивание, JOIN Django построит сам:
values()иvalues_list():Book.objects.values("title", "author__name")вернёт словари.select_relatedздесь не нужен, сvalues()он игнорируется;annotate()сF():annotate(author_name=F("author__name"))оставляет объекты модели и добавляет им атрибут;SubqueryиOuterRef: значение из связанной таблицы по условию, например дата последнего заказа клиента.
Загрузка связанных объектов целиком:
select_related: дляForeignKeyиOneToOneField, один запрос с JOIN. На многие-ко-многим Django выброситFieldError;prefetch_related: дляManyToManyFieldи обратных связей (author.book_set). Отдельный запрос сINпо собранным id, склейка в Python;Prefetch:Prefetch("book_set", queryset=Book.objects.filter(is_published=True), to_attr="published")фильтрует подгружаемое;only()иdefer(): сужают набор колонок, в том числе через связь:select_related("author").only("title", "author__name").
Лишние запросы ловят через django-debug-toolbar, в тестах через assertNumQueries.
Где спотыкаются
Делают prefetch_related("book_set"), а в цикле пишут author.book_set.filter(...). Фильтр создаёт новый запрос на каждого автора, предзагрузка пропадает зря. Нужен Prefetch с queryset.
Используют only(), а потом обращаются к невыбранному полю. Django молча догрузит его отдельным запросом на каждый объект, и N+1 возвращается.
Говорят, что select_related для одного объекта, а prefetch_related для списков. Критерий другой: тип связи и то, во сколько раз JOIN размножит строки.