16

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

в моем 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

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

+0

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

ответ

10

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

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

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 
} 

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

EDIT

Я думаю, что я нашел лучший, более изящный способ сделать это беку создать свой собственный Paginator, чтобы получить поведение, как я использовал, чтобы получить со старым классом Постраничного 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) 

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

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": [] 
} 

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

+0

Да. Ваше решение работает. Это пока не оптимально, так как мы не можем получить ссылки на страницы в API-интерфейсе (и гиперссылки с постраничной привязкой исчезли), но в настоящее время он работает только один. Мы должны быть очень близки. Дайте мне знать, если вы придумаете что-нибудь лучше, иначе я приму ваш ответ через пару дней. Еще раз спасибо. – kstratis

+0

Это тоже правильно. Мы можем сделать почти все что угодно ...! Я считаю, что ключом к решению было то, как вы заменили 'class Meta: object_serializer_class = CourseSerializer' простым конструктором' serializer = CourseSerializer (курсы, many = True) '. В прошлом мы можем позволить нашему воображению взять верх. Во всяком случае, я предпочитаю придерживаться настроек по умолчанию, но поскольку вы дали мне «ключ», я принимаю ваш ответ. Спасибо. – kstratis

20

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

То, что мы должны были использовать с самого начала является это:

Просто используйте встроенную в постраничной навигации и изменить свой 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 controls, которые по каким-то причинам пропускаете сейчас ...

я мог определенно использовать несколько больше предложений по этому вопросу ...

+2

Ничего себе. Неужели это так просто? Они должны действительно обновить документацию на примере. Что касается HTML-ссылок, я не знаю, я никогда не использовал API-интерфейс, доступный для просмотра. – Brobin

+2

круто! это решение избавит меня от времени на проверку исходного кода. Надеюсь, они сделают документ как можно скорее. – haudoing

+0

@haudoing: Сделайте документы, которые хотите видеть в мире! Это открытый исходный код, и документы - отличный способ вернуть. – mlissner

0

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

Django Rest Framework 3.2.3 pagination not working for generics.ListCreateAPIView

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

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

Смежные вопросы