Оптимизация проекта — один из самых недооценённых этапов разработки. Особенно тогда, когда сайт начинает расти: появляются новые модели, связи, шаблоны, сложные ORM-запросы. На этапе написания кода легко не заметить, что Django генерирует десятки лишних SQL-запросов, которые замедляют сайт в разы.
Django Debug Toolbar — это инструмент, который позволяет увидеть сайт “изнутри”: время рендеринга, SQL-запросы, работу шаблонов, статических файлов, middleware и многое другое.
В этом уроке мы:
- установим и настроим Debug Toolbar;
- научимся работать с панелью SQL и искать проблемные запросы;
- разберём реальные примеры оптимизации;
- изучим
select_related()иprefetch_related(); - проверим результаты в браузере;
- выполним практические задания.
Debug Toolbar — это панель разработчика, встроенная в Django-проект.
С её помощью можно:
- количество запросов,
- сами SQL-запросы,
- время выполнения каждого,
- повторяющиеся запросы.
- скорость формирования страницы,
- влияние шаблонов,
- время загрузки контента.
Полезно при ошибках, связанных с middleware, cookie, CORS и т.д.
Например:
- N+1 проблема, когда к каждому объекту Django дополнительно делает запросы (часто в шаблонах).
- медленные JOIN-ы,
- лишние SELECT’ы при работе с ForeignKey и ManyToMany.
Устанавливаем пакет:
pip install django-debug-toolbarТеперь нужно подключить его в проекте.
Откроем settings.py.
INSTALLED_APPS += [
'debug_toolbar',
]Очень важно, чтобы DebugToolbarMiddleware стоял как можно выше, но после django.middleware.common.CommonMiddleware.
MIDDLEWARE += [
'debug_toolbar.middleware.DebugToolbarMiddleware',
]По умолчанию Debug Toolbar работает только для localhost:
INTERNAL_IPS = [
'127.0.0.1',
]Открываем cinemahub/urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('movies.urls')),
path('__debug__/', include('debug_toolbar.urls')),
]Теперь Debug Toolbar будет доступен на всех страницах.
Запускаем сервер:
python manage.py runserverОткрываем главную страницу проекта, например:
http://127.0.0.1:8000/
Справа появится вертикальная панель Debug Toolbar.
Если панель не появилась, причины могут быть следующие:
| Проблема | Причина | Решение |
|---|---|---|
| Панель не видна | Не настроены INTERNAL_IPS |
Нужно указать 127.0.0.1 |
| Страница использует iframe | Debug Toolbar не отображается внутри iframe | Открыть страницу напрямую |
| Проект работает не на localhost | Debug Toolbar заблокирован | Добавить IP в INTERNAL_IPS |
| Middleware не подключён | Пропустили шаг | Проверить порядок MIDDLEWARE |
Открой страницу с выводом фильмов или жанров.
Например, представим, что в приложении movies есть представление:
def movies_list(request):
movies = Movie.objects.all()
return render(request, "movies/list.html", {"movies": movies})В шаблоне:
{% for movie in movies %}
<p>{{ movie.title }} — {{ movie.genre.name }}</p>
{% endfor %}На первый взгляд — всё нормально. Но Debug Toolbar в разделе SQL покажет:
- один запрос на получение фильмов
-
- по одному запросу для каждого
movie.genre
- по одному запросу для каждого
Если фильмов 20 → получаем 21 SQL-запрос.
Это и называется N+1 проблема.
Django предоставляет два основных инструмента оптимизации:
Загружает связанные данные в одном SQL-запросе:
movies = Movie.objects.all().select_related("genre")Теперь запросов: 1 вместо 20+1.
Пример:
movies = Movie.objects.all().prefetch_related("actors")Django выполнит:
- один запрос на фильмы,
- один — на актеров,
- и сопоставит результаты в Python.
Изначальное представление:
def movies_list(request):
movies = Movie.objects.all()
return render(request, "movies/list.html", {"movies": movies})Шаблон выводит:
- жанр (FK)
- теги (ManyToMany)
На 20 фильмов → до 50 SQL-запросов.
Оптимизируем:
def movies_list(request):
movies = (
Movie.objects
.all()
.select_related("genre")
.prefetch_related("tags")
)
return render(request, "movies/list.html", {"movies": movies})-
Обновляем страницу.
-
Открываем Debug Toolbar → SQL.
-
Смотрим:
- количество запросов стало меньше;
- большая часть данных загрузилась одним JOIN-ом.
Movie.objects.select_related("tags") # неверноManyToMany требует prefetch_related.
Movie.objects.prefetch_related("genre") # можно, но бессмысленноЛучше select_related.
Debug Toolbar показывает данные для текущей страницы, не всегда обновлённые.
- Time — как быстро Django сформировал страницу.
- Templates — какие шаблоны использовались.
- Headers — полезно при работе с middleware.
- Static Files — какие статические файлы загружены.
- Signals — отображает сигналы Django.
Если страница грузится медленно, но SQL-запросов мало → проблема может быть:
- в шаблоне,
- в сложных циклах и фильтрах,
- в больших статических файлах.
Добавьте Django Debug Toolbar в проект cinemahub. Проверьте количество SQL-запросов на странице списка фильмов.
Создайте страницу списка жанров. Проверьте, сколько SQL-запросов выполняется на странице.
Оптимизируйте вывод фильмов так, чтобы на странице выполнялось не более трёх SQL-запросов:
- фильмы
- жанры
- теги
Создайте страницу “Фильмы по жанру”. Оптимизируйте ORM-запросы.
Создайте страницу “Фильмы по актёру”. Используйте prefetch_related для оптимизации.
- Для чего используется Django Debug Toolbar?
- Почему возникает проблема N+1 запросов?
- Какие связи оптимизирует select_related?
- Какие связи оптимизирует prefetch_related?
- Что произойдёт, если вызвать select_related для поля ManyToMany?
- Где в Debug Toolbar можно посмотреть SQL-запросы?
- Что показывает панель Templates?
- Почему важно проверять результаты оптимизации в браузере?
- Можно ли использовать select_related с несколькими полями сразу?
- Почему prefetch_related выполняет несколько запросов вместо одного?