web-dev-qa-db-fra.com

Pagination dans Django-Rest-Framework à l'aide d'API-View

J'ai actuellement une configuration de vue API comme suit:

class CartView(APIView):
    authentication_classes = [SessionAuthentication, TokenAuthentication]
    permission_classes = [IsAuthenticated, ]
    api_view = ['GET', 'POST']

    def get(self, request, format=None):
        try:
            cart = request.user.cart
        except Cart.DoesNotExist:
            cart = Cart.objects.create(user=request.user)
        cart_details = cart.cart_details.all()
        serializer = CartDetailSerializer(cart_details, many=True, fields=['id', 'item', 'quantity', 'product_type'])
        return Response(serializer.data)

Ici CartDetailSerializer est un ModelSerializer normal.

Je veux paginer cette API. Cependant, dans la documentation de DRF, j'ai trouvé ceci: 

Si vous utilisez une vue APIView régulière, vous devez appeler vous-même l'API de pagination pour vous assurer de renvoyer une réponse paginée. 

Il n'y a pas d'exemple fourni sur la façon de paginer une API APIView standard.

Quelqu'un peut-il poster un exemple que je peux utiliser dans le scénario ci-dessus.

Merci.

12
apatel

Lorsque vous utilisez APIView standard, vous devez utiliser la propre classe Paginator de Django. 

La pagination de Django dans Views

Dans votre cas, vous pouvez paginer queryset avant de l'envoyer au sérialiseur.

Quelque chose comme ça:

def get(self, request, format=None):
    try:
        cart = request.user.cart
    except Cart.DoesNotExist:
        cart = Cart.objects.create(user=request.user)
    cart_details = cart.cart_details.all()

    paginator = Paginator(cart_details, 10)
    page = request.GET.get('page')

    try:
        cart_details = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        cart_details = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        cart_details = paginator.page(paginator.num_pages)
    serializer = CartDetailSerializer(cart_details, many=True, fields=['id', 'item', 'quantity', 'product_type'])
    return Response(serializer.data)

J'espère que cela t'aides.

12
Aakash Rayate

Bien que rayy mentionne une possibilité, Django-rest-framework peut gérer cela en interne avec certaines fonctionnalités supplémentaires qui facilitent grandement l'utilisation de votre API. (* notez que la pagination de Django-rest-framework est construite à partir de la pagination Django de Django.core.paginator)

Juste après ce que vous avez cité, vous trouverez les informations clés pour résoudre ce problème:

La pagination est effectuée automatiquement uniquement si vous utilisez les vues ou ensembles de vues génériques. Si vous utilisez une vue APIView régulière, vous devez appeler vous-même l'API de pagination pour vous assurer de renvoyer une réponse paginée. Consultez le code source des classes mixins.ListMixin et generics.GenericAPIView pour obtenir un exemple.

Légère correction à ce qui est indiqué ici: regardez le ListModelMixin.

Si vous allez sur ces deux liens, vous pouvez voir le code source des fichiers ci-dessus: generics.pymixins.py

Ce que vous devez faire, c'est inclure quelque chose comme ceci pour que la pagination fonctionne dans APIView (** remarque: ce code n'a pas été testé, mais l'idée est correcte. Il existe également une meilleure façon de l'écrire plutôt que de l'inclure. à tous points de vue mais je vous laisse le soin de garder ma réponse brève et compréhensible):

from __future__ import absolute_import
# if this is where you store your Django-rest-framework settings
from Django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response

from .models import Cart 

class CartView(APIView):
    pagination_class = settings.DEFAULT_PAGINATION_CLASS

    def get(self, request, format=None):
        #assuming every other field in the model has a default value    
        cart = Cart.objects.get_or_create(user=request.user)

        #for a clear example
        cart_details = Cart.objects.all()

        page = self.paginate_queryset(cart_details)
        if page is not None:
            serializer = CartDetailSerializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = CartDetailSerializer(cart_details, many=True)
        return Response(serializer.data)

    @property
    def paginator(self):
        """
        The paginator instance associated with the view, or `None`.
        """
        if not hasattr(self, '_paginator'):
            if self.pagination_class is None:
                self._paginator = None
            else:
                self._paginator = self.pagination_class()
        return self._paginator

    def paginate_queryset(self, queryset):
        """
        Return a single page of results, or `None` if pagination is disabled.
        """
        if self.paginator is None:
            return None
        return self.paginator.paginate_queryset(queryset, self.request, view=self)

    def get_paginated_response(self, data):
        """
        Return a paginated style `Response` object for the given output data.
        """
        assert self.paginator is not None
        return self.paginator.get_paginated_response(data)

J'espère que cela vous a été utile, à vous et aux autres personnes qui ont croisé ce message.

21
prawg

J'utilise DRF version 3.6.2. Vous n'avez pas besoin de beaucoup de code. Utilisez simplement ces étapes simples. 

 class ProductPagination(PageNumberPagination):
        page_size = 5

    class product_api(generics.ListCreateAPIView):    
            queryset = Products.objects.all()
            serializer_class = product_serilizer
            pagination_class = ProductPagination

si vous voulez une fonctionnalité de recherche par méthode, vous pouvez écrire ci-dessous le code

class ProductPagination(PageNumberPagination):
        page_size = 5

class product_api(generics.ListCreateAPIView):
    queryset = Products.objects.all()
    serializer_class = product_serilizer
    pagination_class = SearchProductPagination    

    def get_queryset(self):
        qs = super(product_search_api,self).get_queryset()
        searched_product = self.request.query_params.get('searched_product',None)
        if search:
            qs = Products.objects.filter(Q(product_name__icontains= searched_product))
        return qs
0
Nids Barthwal

Je préfère prolonger la classe Paginator, voici à quoi cela ressemblerait:

from rest_framework import status
from rest_framework.exceptions import NotFound as NotFoundError
from rest_framework.pagination import PageNumberPagination # Any other type works as well
from rest_framework.response import Response
from rest_framework.views import APIView

class CustomPaginator(PageNumberPagination):
    page_size = 10 # Number of objects to return in one page

    def generate_response(self, query_set, serializer_obj, request):
        try:
            page_data = self.paginate_queryset(query_set, request)
        except NotFoundError:
            return Response({"error": "No results found for the requested page"}, status=status.HTTP_400_BAD_REQUEST)

        serialized_page = serializer_obj(page_data, many=True)
        return self.get_paginated_response(serialized_page.data)

class CartView(APIView):

    def get(self, request, format=None):
        cart_details = Cart.objects.filter(user=request.user) # or any other query
        paginator = CustomPaginator()
        response = paginator.generate_response(cart_details, CartDetailSerializer, request)
        return response
0
NFern