В современных 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.
- Обработайте исключения при недоступной странице.
- Кешируйте пагинатор, если объём данных растёт.
