В КУРСЕ?

Разбираемся в теме

Django: почему список из десяти записей иногда отправляет одиннадцать запросов

Страница со списком событий может выглядеть небольшой, но обращаться к базе снова и снова. Причина нередко скрывается в выводе связанных данных: название события уже загружено, а название площадки приходится получать отдельно. Чтобы увидеть такую проблему, полезно считать обращения к базе вместе с тем, какие данные реально использует страница.

От списка к повторяющимся обращениям

Представим учебную афишу: у каждого события есть одна площадка, связь хранится через ForeignKey. Страница показывает десять событий и название площадки для каждого. В упрощённом случае один запрос получает события, а последующее первое обращение к площадке каждого объекта выполняет ещё один запрос. Получается один плюс десять, то есть одиннадцать обращений. Это пример проблемы N+1. Число относится только к описанному чтению данных без предварительной загрузки и отдельного кеширования. Авторизация, подсчёт страниц и другие части приложения способны добавить собственные запросы. Поэтому цифру нельзя считать универсальной характеристикой любой афиши.

Два способа подготовить связанные данные

Когда каждой строке соответствует один связанный объект, select_related позволяет получить нужные поля через соединение таблиц в SQL. Для описанной связи события с площадкой это естественный кандидат. Если же у события много участников, задача другая: prefetch_related выполняет отдельные запросы и связывает результаты в памяти приложения. Он подходит для коллекций, включая отношения многие ко многим. Выбор определяется устройством связи и тем, что будет прочитано. Предварительная загрузка всех возможных отношений увеличивает объём данных и расход памяти. Кроме того, новый фильтр по связанному набору может потребовать нового обращения к базе: подготовленная коллекция не заменяет произвольный будущий запрос.

Проверять итог, а не название метода

Оптимизацию начинают с измерения конкретного сценария, затем повторяют его после изменения. Сравнивают запросы, время и объём возвращаемых данных на одинаковом наборе событий. Сокращение числа запросов полезно, но само по себе не доказывает ускорение: один тяжёлый запрос тоже способен стать проблемой. Необходимо убедиться, что список сохранил правильный порядок, состав и значения полей. Отдельного внимания заслуживают пустой список и события без необязательных связей. Старые примеры Django помогают изучать идею, однако совместимость конкретного поведения проверяют по документации установленной версии. При переносе примера не стоит считать устаревшие настройки готовым решением для рабочего сайта.

Попробуйте на практике

Нарисуйте на бумаге схему чтения учебной афиши. Выполнять запросы к действующей базе для этого упражнения не нужно.

  1. Изобразите десять карточек событий. У каждой отметьте одно поле площадки, которое требуется показать посетителю.
  2. Для варианта без предварительной загрузки нарисуйте одно обращение за событиями и по одному за площадкой каждого объекта. Подпишите условия такого подсчёта.
  3. Постройте второй вариант с select_related для площадки. Объясните, какие данные должны оказаться доступны после чтения списка.
  4. Добавьте к событию коллекцию участников. Отдельно обозначьте, почему для неё рассматривается prefetch_related, и составьте план измерения до и после изменения.

Как проверить результат. Для исходного упрощённого сценария получено одиннадцать обращений, для загрузки событий вместе с площадкой через соединение одно. Коллекция участников не объявлена одиночной связью, а план проверки включает правильность данных и измерение времени.

Частые вопросы

Нужно ли добавлять оба метода в каждый запрос?

Нет. Загружать заранее стоит только отношения, которые действительно понадобятся. Лишние связанные данные могут увеличить объём работы, даже если последующих обращений к базе станет меньше.

Почему после prefetch_related всё ещё появляются запросы?

Проверьте, читает ли код именно подготовленную коллекцию. Дополнительная фильтрация или иной запрос по связи могут обходить ожидаемое использование её кеша. Причину устанавливают по фактическим обращениям, а не по наличию метода в цепочке.

Самостоятельный разбор темы. Содержание конкретной обучающей программы здесь не представлено.

Зарегистрируйтесь, чтобы уточнить возможность доступа к этому материалу

Зарегистрироваться
← К списку материалов