В современных API часто требуется разбивать большие наборы данных на страницы, чтобы снизить нагрузку на клиент и сервер. В Django REST Framework (DRF) существует встроенная поддержка пагинации, но иногда хочется использовать готовый Django Paginator и гибко управлять лимитом и смещением. В этой статье разберём, как подключить pagination drf django paginator и настроить limit/offset для вашего API. Вы получите готовый шаблон, который можно быстро интегрировать в любой проект без лишних усилий.

Разбор задачи

Пагинация нужна, когда количество записей в базе превышает разумный лимит, например 1000+ строк. Если не ограничить запросы, клиент получит огромный ответ, что замедлит работу и потребует больших ресурсов. DRF предлагает несколько стратегий: PageNumberPagination, LimitOffsetPagination, CursorPagination. Однако иногда хочется использовать Django Paginator, чтобы иметь полный контроль над выборкой и логику смещения в одном месте. В этом случае важно правильно связать DRF ViewSet с Django Paginator, чтобы получить единый и надёжный механизм.

Пошаговое решение

1. Создаём новый класс пагинации, наследующий BasePagination из DRF и использующий DjangoPaginator внутри. Внутри метода paginate_queryset вы берёте параметры limit и offset из запроса и передаёте их в DjangoPaginator. 2. В paginate_queryset создаём объект DjangoPaginator(queryset, page_size), где page_size равен лимиту. 3. Переходим к нужному диапазону с помощью page_obj = paginator.page(page_number), где page_number = offset // limit + 1. 4. Возвращаем page_obj.object_list как отсортированный список, а в get_paginated_response формируем ответ в формате DRF, добавляя заголовки X-Total-Count и Link для удобства клиента. 5. Настраиваем ViewSet, указывая pagination_class = CustomPagination, и в настройках проекта прописываем PAGINATION_CLASS = 'myapp.pagination.CustomPagination'. 6. Тестируем с помощью curl и проверяем, что параметры limit и offset работают корректно, а в ответе присутствует информация о количестве всех записей и следующей странице. 7. Если понадобится, можно добавить дополнительные фильтры, чтобы пагинация работала в сочетании с поиском и сортировкой.

Рабочий код

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
from django.core.paginator import Paginator
from .models import Item
from .serializers import ItemSerializer

# Пагинация через PageNumberPagination, но с кастомным Django Paginator
class DjangoPaginatorPageNumber(PageNumberPagination):
    page_size_query_param = 'page_size'
    max_page_size = 100
    def paginate_queryset(self, queryset, request, view=None):
        self.request = request
        paginator = Paginator(queryset, self.get_page_size(request))
        page_number = request.query_params.get(self.page_query_param, 1)
        try:
            page = paginator.page(page_number)
        except Exception:
            page = paginator.page(1)
        self.page = page
        self.page_size = paginator.per_page
        return list(page.object_list)
    def get_paginated_response(self, data):
        return Response({
            'count': self.page.paginator.count,
            'total_pages': self.page.paginator.num_pages,
            'next': self.get_next_link(),
            'previous': self.get_previous_link(),
            'results': data,
        })

# ViewSet с использованием DjangoPaginatorPageNumber
class ItemViewSet(ModelViewSet):
    queryset = Item.objects.all().order_by('id')
    serializer_class = ItemSerializer
    pagination_class = DjangoPaginatorPageNumber

Типичные ошибки

1. Перепутать PageNumberPagination с LimitOffsetPagination – тогда параметры page и limit конфликтуют.
2. Не задавать page_size_query_param – клиент не сможет изменить размер страницы.
3. Ошибка в расчёте page_number из query_params – неверный тип приводит к исключению, лучше использовать try/except.
4. Не использовать .order_by() – пагинация без порядка ведёт к непредсказуемым результатам.
5. Отказ от кеширования пагинатора на стороне Django – при больших наборах запросы становятся медленными.

FAQ

Как изменить размер страницы по умолчанию?

Переопределите метод get_page_size() в DjangoPaginatorPageNumber или установите page_size_query_param и max_page_size.

Можно ли использовать одновременно лимит и offset с Django Paginator?

Да, но нужно написать свой класс, который объединяет оба подхода – либо использовать LimitOffsetPagination, либо реализовать собственный логик.

Что делать, если количество страниц слишком велико?

Включите кеширование результата пагинации (например, Redis) и ограничьте max_page_size, чтобы не отдавать слишком много страниц за один запрос.

Заключение

Пагинация в DRF – ключ к производительности и удобству API. Используя Django Paginator можно контролировать количество объектов на странице и получать точные метаданные. Чтобы всё работало без ошибок, придерживайтесь чек‑листа:

  • Укажите корректный class‑viewset и pagination_class.
  • Проверьте порядок queryset с .order_by().
  • Добавьте page_size_query_param и max_page_size.
  • Обработайте исключения при недоступной странице.
  • Кешируйте пагинатор, если объём данных растёт.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *