Оптимизация сайта с Django Debug Toolbar

Курс по Django: https://stepik.org/a/183363

Архив проекта: 36_sitewomen.zip

К этому моменту мы с вами создали небольшой, но функциональный сайт с отображением статей, в том числе по категориям и тегам. И, естественно, возникает вопрос, насколько хорошо работает приложение нашего сайта? Здесь следует пояснить, что значит «хорошо работает». В данном случае, я рассматриваю:

  • скорость работы приложения;
  • нагрузку на СУБД (частоту и сложность запросов);
  • корректность возвращаемых пользователю данных.

Чтобы отслеживать эти и некоторые другие характеристики разрабатываемой программы, в Django имеется довольно удобный инструмент под названием:

Django Debug Toolbar

Давайте посмотрим, как его можно использовать для анализа работы нашего приложения.

Если перейти в репозиторий pypi.org и в поиске ввести «django debug toolbar», то увидим различные версии этого пакета. Установим последнюю из них в виртуальное окружение. Для этого, в терминале выполним команду:

pip install django-debug-toolbar

После установки, его нужно «прикрутить» к нашему сайту. Подробная информация о его настройки доступна на странице:

https://django-debug-toolbar.readthedocs.io/en/latest/installation.html

Согласно этой документации, в файле settings.py нужно зарегистрировать это приложение, добавив в INSTALLED_APPS строчку:

INSTALLED_APPS = [
    ...
    'debug_toolbar',
]

В этом же файле в коллекции MIDDLEWARE нужно прописать:

MIDDLEWARE = [
    ...
    'debug_toolbar.middleware.DebugToolbarMiddleware',
]

Далее, в этом же файле нужно прописать новую коллекцию INTERNAL_IPS со списком отслеживаемых IP-адресов

INTERNAL_IPS = [
    '127.0.0.1',
]

Переходим в файл urls.py пакета конфигурации и добавляем следующий маршрут:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('women.urls')),
    path("__debug__/", include("debug_toolbar.urls")),
]

Все, инструмент добавлен и давайте посмотрим, как он будет выглядеть и что показывать. Запускаем тестовый веб-сервер, переходим на главную страницу сайта и справа отображается панель Debug Toolbar.

Здесь мы видим версию Django, время формирования страницы, количество выполненных SQL-запросов, список используемых шаблонов и так далее. В целом, это довольно удобный и информативный инструмент. Если нужна более детальная информация, например, по SQL-запросам, то достаточно кликнуть по этому пункту и появится развернутая информация. В частности, здесь видим наши запросы, прописанные на уровне ORM-команд. Разумеется, здесь мы их видим уже на уровне SQL-запросов к конкретной СУБД (SQLite).

Уникальные запросы нас вполне устраивают, они необходимы и не происходит дублирования. А вот ниже видим однотипные запросы, что нехорошо. За что они отвечают. Смотрим запрос и видим, что идет чтение названия рубрики. Очевидно, это связано с обращением к категории в шаблоне index.html через переменную p:

<p class="first">Категория: {{p.cat.name}}</p>

Давайте проверим, так ли это. Уберем временно эту строчку, обновим страницу, и действительно, число запросов заметно уменьшилось, а скорость загрузки страницы увеличилась. Почему это происходит? Вспоминаем, что в Django есть понятие «ленивых» и «жадных» запросов. Ленивые, то есть, отложенные запросы, которые выполняются только в момент непосредственного обращения к данным, например, для вывода рубрики. Как раз здесь выполняется отложенный запрос. Они достаточно удобны при работе с единичными записями, но когда их много, то возникает неприятный эффект резкого увеличения числа SQL-запросов, что мы сейчас и наблюдаем.

Как можно оптимизировать этот процесс? Очевидно, нужно сделать загрузку категорий и постов одним запросом. Для этого в Django имеются два полезных метода:

  • select_related(key) – «жадная» загрузка связанных данных по внешнему ключу key, который имеет тип ForeignKey;
  • prefetch_related(key) – «жадная» загрузка связанных данных по внешнему ключу key, который имеет тип ManyToManyField.

Как и где их использовать. Смотрите, за главную страницу у нас отвечает функция представления index() и в ней происходит чтение опубликованных постов. Как раз здесь, после метода all() мы и запишем метод select_related следующим образом:

def index(request):
    data = {
        'title': 'Главная страница',
        'menu': menu,
        'posts': Women.published.all().select_related('cat'),
        'cat_selected': 0,
    }

В этом методе указываем атрибут cat, который определен во вторичной модели Women как ForeignKey для связки с первичной моделью Category. Теперь, мы имеем «жадную» загрузку дополнительных данных из таблицы category и для получения названия рубрики нет необходимости делать запрос к БД.

Давайте посмотрим, как это будет работать. Возвращаемся в браузер, обновляем главную страницу и видим, что запросов действительно стало меньше. Вот так, благодаря Debug Toolbar и использованию «жадного» запроса мы оптимизировали работу нашего сайта.

Аналогично правим функции представления show_category() и show_tag_postlist() для оптимизации при отображении списка статей по отдельным рубрикам.

Как видите, Debug Toolbar довольно полезный инструмент для анализа работы сайта и я советую вам также использовать его или какой-либо аналогичный, чтобы создавать оптимизированные сайты.

Курс по Django: https://stepik.org/a/183363

Видео по теме