web-dev-qa-db-ja.com

Django Rest Framework3.1はページネーションを中断します。Pag​​inationSerializer

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

何か案は?

前もって感謝します

17
kstratis

これが完全に正しい方法かどうかはわかりませんが、私のニーズには合っています。 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)

これが私のコースシリアライザーを使用するハッキングされたシリアライザーです。

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を使用してこれを行うには、まだ良い方法が必要だと思いますが、十分に文書化されていません。それ以上のことがわかったら、投稿を編集します。

編集

以前のPaginatedSerializerクラスで行っていたような動作を実現するために、独自のカスタム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": []
}

これがBrowsableAPIでどのように機能するかはまだわかりません(drfのこの機能は使用していません)。このために独自のカスタムクラスを作成することもできると思います。これがお役に立てば幸いです。

11
Brobin

私はそれを理解したと思います(少なくともほとんどの場合):

最初から使用すべきだったのは次のとおりです。

組み込みのページネーターを使用して、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_SIZEsettings.pyに設定するだけです。

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

これで、更新前と同じように、応答の本文にあるすべてのオプション(カウント、次のリンク、およびバックリンク)が順序付けられた状態になりました。

ただし、まだ私を悩ませていることがもう1つあります。新しい htmlページネーションコントロール を取得できるはずです。今のところいくつかの理由が欠けています...

私は間違いなくこれについてさらにいくつかの提案を使用することができます...

22
kstratis

これが投稿されてから1年以上経ちましたが、これが他の人の役に立つことを願っています。私の同様の質問への回答は私にとっての解決策でした。 DRF3.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

0
cjukjones