Стили пагинации, кастомная пагинация, настройка вывода
Пагинация разбивает большие наборы данных на страницы. DRF предоставляет несколько стилей пагинации и позволяет создавать кастомные.
Глобальная настройка в settings.py:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20, # Количество записей на странице по умолчанию
}Пагинация по номеру страницы через параметр ?page=:
from rest_framework.pagination import PageNumberPagination
class ArticleList(generics.ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
pagination_class = PageNumberPaginationЗапросы:
GET /articles/?page=1 — первая страницаGET /articles/?page=2 — вторая страницаОтвет:
{
"count": 150,
"next": "http://example.com/articles/?page=2",
"previous": null,
"results": [
{...},
{...}
]
}from rest_framework.pagination import PageNumberPagination
class CustomPageNumberPagination(PageNumberPagination):
page_size = 20 # Записей на странице по умолчанию
page_size_query_param = 'page_size' # Параметр для изменения размера
max_page_size = 100 # Максимальный размер страницы
page_query_param = 'page' # Параметр номера страницы
# views.py
class ArticleList(generics.ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
pagination_class = CustomPageNumberPaginationЗапросы:
GET /articles/?page=2&page_size=50 — страница 2, по 50 записейПагинация через лимит и смещение:
from rest_framework.pagination import LimitOffsetPagination
class ArticleList(generics.ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
pagination_class = LimitOffsetPaginationПараметры:
limit — количество записейoffset — смещение от началаЗапросы:
GET /articles/?limit=20&offset=0 — первые 20GET /articles/?limit=20&offset=20 — следующие 20 (страница 2)GET /articles/?limit=10&offset=50 — 10 записей, начиная с 51-йОтвет:
{
"count": 150,
"next": "http://example.com/articles/?limit=20&offset=20",
"previous": null,
"results": [...]
}class CustomLimitOffsetPagination(LimitOffsetPagination):
default_limit = 20 # Записей по умолчанию
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = 100 # Максимальный лимитПагинация на основе курсора — указателя на позицию:
from rest_framework.pagination import CursorPagination
class CreatedCursorPagination(CursorPagination):
ordering = '-created_at' # Обязательное поле
page_size = 20
page_size_query_param = 'page_size'
max_page_size = 100
# views.py
class ArticleList(generics.ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
pagination_class = CreatedCursorPaginationЗапросы:
GET /articles/?cursor=cD0yMDI0LTAxLTAx — следующая страницаОтвет:
{
"next": "http://example.com/articles/?cursor=cD0yMDI0LTAxLTAx",
"previous": null,
"results": [...]
}Преимущества CursorPagination:
Недостатки:
class ArticleList(generics.ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
pagination_class = None # Отключить пагинациюREST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': None,
}Создание своего класса пагинации:
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
class CustomPagination(PageNumberPagination):
page_size = 20
page_size_query_param = 'size'
max_page_size = 100
def get_paginated_response(self, data):
"""Кастомный формат ответа"""
return Response({
'meta': {
'total': self.page.paginator.count,
'page': self.page.number,
'per_page': self.page_size,
'total_pages': self.page.paginator.num_pages,
'has_next': self.page.has_next(),
'has_prev': self.page.has_previous(),
},
'data': data
})Ответ:
{
"meta": {
"total": 150,
"page": 2,
"per_page": 20,
"total_pages": 8,
"has_next": true,
"has_prev": true
},
"data": [...]
}Более подробный ответ с информацией о странице:
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from collections import OrderedDict
class DetailedPagination(PageNumberPagination):
page_size = 20
page_size_query_param = 'size'
max_page_size = 100
def get_paginated_response(self, data):
return Response(OrderedDict([
('count', self.page.paginator.count),
('next', self.get_next_link()),
('previous', self.get_previous_link()),
('results', data),
('page', self.page.number),
('page_size', self.page_size),
('total_pages', self.page.paginator.num_pages),
]))from rest_framework import viewsets
from rest_framework.pagination import PageNumberPagination
class ArticlePagination(PageNumberPagination):
page_size = 20
page_size_query_param = 'size'
max_page_size = 100
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
pagination_class = ArticlePaginationclass FlexiblePagination(PageNumberPagination):
def __init__(self):
super().__init__()
self.page_size = 20
def get_page_size(self, request):
"""Динамический размер страницы"""
if request.query_params.get('full'):
return None # Без пагинации
return super().get_page_size(request)
class ArticleList(generics.ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
pagination_class = FlexiblePaginationЗапрос:
GET /articles/ — с пагинацией (20 записей)GET /articles/?full=true — без пагинации (все записи)| Стиль | Параметры | Плюсы | Минусы | Когда использовать |
|---|---|---|---|---|
| PageNumber | ?page= | Простота, понятность | Нестабильность при изменении данных | Блоги, каталоги |
| LimitOffset | ?limit=&offset= | Гибкость, произвольный доступ | Медленно на больших offset | Админки, таблицы |
| Cursor | ?cursor= | Стабильность, производительность | Нет произвольного доступа | Ленты, чаты, большие данные |
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.