Django Rest Framework 3.1 разбивает pagination.PaginationSerializer

Я только что обновился до Django Rest Framework 3.1, и кажется, что все ад сломался.

в моем serializers.py У меня был следующий код:

class TaskSerializer(serializers.ModelSerializer):
    class Meta:
    model = task
    exclude = ('key', ...)

class PaginatedTaskSerializer(pagination.PaginationSerializer):
    class Meta:
        object_serializer_class = TaskSerializer

который работал очень хорошо. Теперь с выпуском 3.1 я не могу найти примеры того, как делать то же самое, поскольку PaginationSerializer больше не существует. Я попробовал подкласс PageNumberPagination и использовал его методы paginate_queryset и get_paginated_response по умолчанию, но я больше не могу их сериализовать.

Другими словами, моя проблема в том, что я больше не могу этого делать:

class Meta:
    object_serializer_class = TaskSerializer

Любые идеи?

Заранее спасибо

Ответы

Ответ 1

Я не уверен, что это абсолютно правильный способ сделать это, но он работает для моих нужд. Он использует Django Paginator и настраиваемый сериализатор.

Вот мой класс просмотра, который извлекает объекты для сериализации

class CourseListView(AuthView):
    def get(self, request, format=None):
        """
        Returns a JSON response with a listing of course objects
        """
        courses = Course.objects.order_by('name').all()
        serializer = PaginatedCourseSerializer(courses, request, 25)
        return Response(serializer.data)

Вот взломанный Serializer, который использует мой сериализатор курса.

from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage

class PaginatedCourseSerializer():
    def __init__(self, courses, request, num):
        paginator = Paginator(courses, num)
        page = request.QUERY_PARAMS.get('page')
        try:
            courses = paginator.page(page)
        except PageNotAnInteger:
            courses = paginator.page(1)
        except EmptyPage:
            courses = paginator.page(paginator.num_pages)
        count = paginator.count

        previous = None if not courses.has_previous() else courses.previous_page_number()
        next = None if not courses.has_next() else courses.next_page_number()
        serializer = CourseSerializer(courses, many=True)
        self.data = {'count':count,'previous':previous,
                 'next':next,'courses':serializer.data}

Это дает мне результат, похожий на поведение, которое дала старая страница.

{
    "previous": 1,
    "next": 3,
    "courses": [...],
    "count": 384
}

Надеюсь, это поможет. Я все еще думаю, что для этого потребуется новый способ, но он просто не документирован. Если я что-нибудь придумаю, я отредактирую свой пост.

ИЗМЕНИТЬ

Я думаю, что нашел лучший, более элегантный способ сделать это, создав свой собственный пользовательский пагайнер, чтобы получить поведение, подобное тому, которое я использовал для старого класса Paginated Serializer.

Это настраиваемый класс paginator. Я перегрузил методы ответа и следующей страницы, чтобы получить результат, который я хочу (т.е. ?page=2 вместо полного URL-адреса).

from rest_framework.response import Response
from rest_framework.utils.urls import replace_query_param

class CustomCoursePaginator(pagination.PageNumberPagination):
    def get_paginated_response(self, data):
        return Response({'count': self.page.paginator.count,
                         'next': self.get_next_link(),
                         'previous': self.get_previous_link(),
                         'courses': data})

    def get_next_link(self):
        if not self.page.has_next():
            return None
        page_number = self.page.next_page_number()
        return replace_query_param('', self.page_query_param, page_number)

    def get_previous_link(self):
        if not self.page.has_previous():
            return None
        page_number = self.page.previous_page_number()
        return replace_query_param('', self.page_query_param, page_number)

Затем мой курс очень похож на то, как вы его реализовали, только на этот раз с помощью пользовательского paginator.

class CourseListView(AuthView):
    def get(self, request, format=None):
        """
        Returns a JSON response with a listing of course objects
        """
        courses = Course.objects.order_by('name').all()
        paginator = CustomCoursePaginator()
        result_page = paginator.paginate_queryset(courses, request)
        serializer = CourseSerializer(result_page, many=True)
        return paginator.get_paginated_response(serializer.data)

Теперь я получаю результат, который я ищу.

{
    "count": 384,
    "next": "?page=3",
    "previous": "?page=1",
    "courses": []
}

Я до сих пор не уверен в том, как это работает для браузера API (я не использую эту функцию для drf). Я думаю, вы также можете создать свой собственный собственный класс для этого. Надеюсь, это поможет!

Ответ 2

Я думаю, что понял (по большей части, по крайней мере):

То, что мы должны использовать с самого начала, следующее:

Просто используйте встроенный paginator и измените views.py на это:

from rest_framework.pagination import PageNumberPagination

class CourseListView(AuthView):
    def get(self, request, format=None):
        """
        Returns a JSON response with a listing of course objects
        """
        courses = Course.objects.order_by('name').all()
        paginator = PageNumberPagination()
        # From the docs:
        # The paginate_queryset method is passed the initial queryset 
        # and should return an iterable object that contains only the 
        # data in the requested page.
        result_page = paginator.paginate_queryset(courses, request)
        # Now we just have to serialize the data just like you suggested.
        serializer = CourseSerializer(result_page, many=True)
        # From the docs:
        # The get_paginated_response method is passed the serialized page 
        # data and should return a Response instance.
        return paginator.get_paginated_response(serializer.data)

Для желаемого размера страницы просто установите PAGE_SIZE в settings.py:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 15
}

Теперь вы должны быть настроены со всеми параметрами, присутствующими в тексте ответа (счетчик, следующий и обратный ссылки), заказанные так же, как и до обновления.

Однако есть еще одна вещь, которая все еще беспокоит меня: мы также должны иметь возможность получать новые html элементы управления pagination, которые для некоторых причина отсутствует на данный момент...

Я мог бы определенно использовать еще пару предложений по этому поводу.

Ответ 3

Я понимаю, что прошло уже год, так как это было опубликовано, но надеемся, что это поможет другим. Ответ на мой подобный вопрос был решением для меня. Я использую DRF 3.2.3.

Django Rest Framework 3.2.3 разбиение на страницы не работает для generics.ListCreateAPIView

Посмотрев, как это было реализовано, я получил решение, необходимое для разбиения на страницы + элементов управления в видимом API.

https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/mixins.py#L39