Пагинация
Зачем пагинация в API
Отдавать все записи разом нельзя: тысячи постов — это мегабайты JSON и медленный запрос. API отдаёт данные порциями и сообщает клиенту, где следующая порция.
Глобальная настройка
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 10,
}
Теперь все списки (generic views и ViewSet) пагинируются:
{
"count": 57,
"next": "http://127.0.0.1:8000/api/posts/?page=3",
"previous": "http://127.0.0.1:8000/api/posts/?page=1",
"results": [
{"id": 41, "title": "..."},
...
]
}
Три стиля пагинации
PageNumberPagination — по номерам страниц
GET /api/posts/?page=2 — привычно, как на сайте.
LimitOffsetPagination — смещение и количество
GET /api/posts/?limit=5&offset=10 — 5 записей, пропустив первые 10. Удобно, когда клиент сам решает, сколько загружать.
CursorPagination — по курсору
GET /api/posts/?cursor=cD0yMDI2LTEwLTI1 — непрозрачный указатель на позицию.
- Стабильна при добавлении новых записей (нет дубликатов и пропусков в бесконечной ленте).
- Быстрая на огромных таблицах (не использует
OFFSET). - Нельзя перейти на произвольную страницу; нужна сортировка по уникальному неизменному полю (по умолчанию
-created).
class FeedPagination(CursorPagination):
page_size = 20
ordering = "-publish"
Свой класс пагинации
from rest_framework.pagination import PageNumberPagination
class StandardPagination(PageNumberPagination):
page_size = 20
page_size_query_param = "page_size" # ?page_size=50
max_page_size = 100 # защита от ?page_size=1000000
Глобально — "DEFAULT_PAGINATION_CLASS": "blog.api.pagination.StandardPagination", или для одного представления:
class PostViewSet(viewsets.ModelViewSet):
pagination_class = StandardPagination
class TagListView(generics.ListAPIView):
pagination_class = None # отключить для короткого списка
Свой формат ответа
Например, добавить число страниц:
from rest_framework.response import Response
class StandardPagination(PageNumberPagination):
page_size = 20
def get_paginated_response(self, data):
return Response({
"count": self.page.paginator.count,
"total_pages": self.page.paginator.num_pages,
"current": self.page.number,
"next": self.get_next_link(),
"previous": self.get_previous_link(),
"results": data,
})
Пагинация в своих APIView
Generic-классы пагинируют сами. В APIView — вручную:
class PostList(APIView):
def get(self, request):
paginator = StandardPagination()
page = paginator.paginate_queryset(Post.published.all(), request, view=self)
serializer = PostSerializer(page, many=True)
return paginator.get_paginated_response(serializer.data)
Итог
- Пагинация включается глобально (
DEFAULT_PAGINATION_CLASS,PAGE_SIZE) или для класса (pagination_class). PageNumberPagination—?page=,LimitOffsetPagination—?limit=&offset=,CursorPagination— для лент.- Ответ содержит
count,next,previous,results; формат можно изменить вget_paginated_response. - Ограничивайте
max_page_size, если клиенту разрешено менять размер страницы.
Практика решено 0 из 5
Включите пагинацию глобально в настройках DRF: класс rest_framework.pagination.PageNumberPagination,
размер страницы — 10.
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 10,
}
Какие ключи есть в ответе списка с PageNumberPagination?
Создайте класс StandardPagination (наследник PageNumberPagination): по 20 объектов,
клиент может менять размер параметром page_size, но не больше 100.
from rest_framework.pagination import PageNumberPagination
class StandardPagination(PageNumberPagination):
page_size = 20
page_size_query_param = "page_size"
max_page_size = 100
Какой тип пагинации лучше подходит для бесконечной ленты, где постоянно появляются новые записи?
Какой адрес вернёт 5 постов, начиная с 11-го, при LimitOffsetPagination? (для /api/posts/)
Войдите, чтобы отмечать пройденные уроки.