📖 Теория — Урок 38

← К оглавлению урока

⚡ Ключевые концепции урока

  • pagination_class — атрибут представления, задаёт класс пагинации
  • PageNumberPagination — по номерам страниц (?page=2&page_size=5)
  • LimitOffsetPagination — по смещению (?limit=5&offset=10)
  • CursorPagination — курсорная, курсор зашифрован, самая безопасная
  • DEFAULT_PAGINATION_CLASS — глобальная пагинация для всех представлений
  • LOGGING — словарь в settings.py: handlers, loggers, formatters
  • django.db.backends — логгер SQL-запросов, уровень DEBUG

Часть 1: Атрибут pagination_class

В Django REST Framework атрибут pagination_class позволяет настраивать механизм постраничной навигации (пагинации) для API. Пагинация помогает разделить результаты запросов на страницы, облегчая управление и отображение больших наборов данных.

Основное назначение

  • Постраничная навигация: делит результаты запросов на страницы для удобного отображения
  • Настройка размера страницы: позволяет задать количество элементов на странице
  • Гибкость: поддерживает встроенные и кастомные классы пагинации

Встроенные классы пагинации

КлассОписаниеПараметры запроса
PageNumberPagination Навигация по номерам страниц и размеру страницы ?page=2&page_size=5
LimitOffsetPagination Начальное смещение и лимит записей ?limit=5&offset=10
CursorPagination Курсорная навигация — зашифрованный указатель позиции ?cursor=bz0x...

Часть 1а: PageNumberPagination

Пагинация с указанием номера страницы. Клиент запрашивает данные, указывая номер страницы (?page=2) и при необходимости размер страницы (?page_size=3).

Создание кастомного класса и подключение к представлению

# views.py
from rest_framework.generics import ListAPIView
from rest_framework.pagination import PageNumberPagination
from .models import Book
from .serializers import BookSerializer

class BookPagination(PageNumberPagination):
    page_size = 5                        # количество элементов на странице по умолчанию
    page_size_query_param = 'page_size'  # клиент может указать размер через ?page_size=N
    max_page_size = 100                  # ограничение максимального размера страницы

class BookListCreateView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    pagination_class = BookPagination    # подключаем наш класс

Атрибуты BookPagination

АтрибутОписание
page_size Количество элементов на странице по умолчанию (здесь 5)
page_size_query_param Параметр запроса для задания размера страницы клиентом
max_page_size Максимально допустимый размер страницы (здесь 100)

Примеры запросов

# Первая страница с размером по умолчанию (5 элементов)
GET http://127.0.0.1:8000/books/

# Вторая страница
GET http://127.0.0.1:8000/books/?page=2

# Вторая страница с 3 элементами на странице
GET http://127.0.0.1:8000/books/?page=2&page_size=3

Часть 1б: LimitOffsetPagination

Пагинация с указанием начального смещения (offset) и лимита (limit) количества записей. Удобна, когда нужна произвольная «прокрутка» по набору данных.

# views.py
from rest_framework.generics import ListAPIView
from rest_framework.pagination import LimitOffsetPagination
from .models import Book
from .serializers import BookSerializer

class BookListView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    pagination_class = LimitOffsetPagination  # используем напрямую
# Получить 5 книг, начиная с 11-й (пропустить первые 10)
GET http://127.0.0.1:8000/books/?limit=5&offset=10
Параметры:
  • limit — максимальное количество элементов на странице
  • offset — начальное смещение от первого элемента (0-based)

Часть 1в: CursorPagination

CursorPagination — один из типов пагинации в DRF, который обеспечивает стабильную и безопасную навигацию по страницам данных. В отличие от других методов, использует курсоры для определения положения в наборе данных — особенно полезно при работе с динамически изменяющимися данными.

Основные преимущества

  • Стабильная пагинация: порядок не «ломается», даже если данные добавляются или удаляются между запросами
  • Безопасность: курсоры шифруются, что предотвращает манипуляции с параметрами
  • Использование курсоров: курсор указывает конкретное положение в наборе данных
# views.py
from rest_framework.generics import ListAPIView
from rest_framework.pagination import CursorPagination
from .models import Book
from .serializers import BookSerializer

class BookCursorPagination(CursorPagination):
    page_size = 5
    ordering = 'published_date'  # поле для определения позиции курсора

class BookListView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    pagination_class = BookCursorPagination

Пример ответа API

GET http://127.0.0.1:8000/books/

{
    "next": "http://127.0.0.1:8000/books/?cursor=bz0xJnA9MjAyMS0wMS0wMQ%3D%3D",
    "previous": null,
    "results": [
        {
            "id": 22,
            "title": "The Great Gatsby",
            "author": "F. Scott Fitzgerald",
            "published_date": "1925-04-10",
            "price": "10.23",
            "is_bestseller": false
        },
        ...
    ]
}
# Следующая страница через зашифрованный курсор
GET http://127.0.0.1:8000/books/?cursor=bz0xJnA9MjAyMS0wMS0wMQ%3D%3D
Поле ordering: по умолчанию CursorPagination ищет поле created_at. Если его нет в модели — создайте кастомный класс и укажите ordering явно. Без этого при глобальной настройке сервер упадёт с ошибкой.

Часть 2: Глобальная настройка пагинации

В DRF можно настроить глобальные параметры пагинации, которые будут применяться ко всем представлениям API. Это позволяет централизованно управлять поведением пагинации без дублирования настроек в каждом представлении.

Основные преимущества

  • Централизованное управление: настройки задаются в одном месте в settings.py
  • Консистентность: все представления следуют единым правилам пагинации
  • Гибкость: можно переопределить глобальные параметры в конкретном представлении

PageNumberPagination — глобально

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 5,  # размер страницы по умолчанию
}

LimitOffsetPagination — глобально

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 5,  # лимит по умолчанию
}

CursorPagination — глобально (требует кастомного класса)

По умолчанию CursorPagination ищет поле created_at для создания курсора. Если такого поля нет — необходимо создать кастомный класс:

# pagination.py (отдельный файл в приложении)
from rest_framework.pagination import CursorPagination

class CustomCursorPagination(CursorPagination):
    page_size = 5
    ordering = 'published_date'  # поле, которое реально существует в модели
# settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'first_app.pagination.CustomCursorPagination',
    'PAGE_SIZE': 5,
}

Переопределение в конкретном представлении

# views.py — переопределение глобальной настройки для конкретного представления
class SpecialBookListView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    pagination_class = None  # отключить пагинацию для этого представления

# или задать другой класс:
class SmallPageBookListView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    pagination_class = BookPagination  # своя локальная настройка

Часть 3: Логирование запросов в базу данных

Логирование SQL-запросов помогает отлаживать и оптимизировать работу приложения: выявляет медленные запросы, анализирует поведение и отслеживает ошибки. В Django настройка логирования выполняется через словарь LOGGING в settings.py.

Основные концепции

  • Логирование SQL-запросов: все SQL-запросы, выполняемые приложением, можно записывать для отладки и мониторинга
  • Настройка логирования: стандартные механизмы Django через словарь LOGGING
  • Анализ логов: обработка для выявления проблем с производительностью

Шаг 1: Включение отладочного режима

Для работы логгера SQL-запросов необходим DEBUG = True. В продакшн-среде SQL-запросы не логируются через django.db.backends при DEBUG = False — это ограничение Django.

# settings.py
DEBUG = True

Шаг 2: Настройка LOGGING

Словарь LOGGING имеет четыре ключевых секции:

СекцияНазначение
version Версия схемы конфигурации, всегда 1
disable_existing_loggers Если True — отключатся встроенные Django-логгеры. Почти всегда False
handlers Куда записывать логи: консоль (StreamHandler) или файл (FileHandler)
loggers Какие компоненты логировать и с каким уровнем

Полная конфигурация LOGGING для SQL-запросов

# settings.py
import os

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
        },
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': os.path.join(BASE_DIR, 'db.log'),  # путь к файлу логов
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
        },
    },
}

Объяснение секций

ЭлементКласс / значениеОписание
handler console logging.StreamHandler Вывод логов в стандартный поток (консоль/терминал)
handler file logging.FileHandler Запись логов в файл db.log
logger django.db.backends level DEBUG Логирует все SQL-запросы при DEBUG = True
Результат: при выполнении запросов к базе данных все SQL-запросы будут выводиться в консоль и записываться в файл db.log в корне проекта. Это позволяет отслеживать и анализировать выполнение запросов, выявлять N+1 проблемы и узкие места.

Уровни логирования

УровеньЧисловое значениеКогда использовать
DEBUG10Все события, включая SQL-запросы
INFO20Информационные сообщения о работе
WARNING30Предупреждения, но работа продолжается
ERROR40Ошибки, требующие внимания
CRITICAL50Критические ошибки, работа может остановиться