Django rest framework override page_size in ViewSet

Question:

I am having problem with django rest framework pagination.
I have set pagination in settings like –

'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 1

Below is my viewset.

class HobbyCategoryViewSet(viewsets.ModelViewSet):    
    serializer_class = HobbyCategorySerializer
    queryset = UserHobbyCategory.objects.all()

I want to set different page size for this viewset. I have tried setting page_size and Paginate_by class variables but list is paginated according to PAGE_SIZE defined in settings. Any idea where I am wrong ?

Asked By: Mangesh

||

Answers:

I fixed this by creating custom pagination class. and setting desired pagesize in class. I have used this class as pagination_class in my viewset.

from rest_framework import pagination

class ExamplePagination(pagination.PageNumberPagination):       
       page_size = 2

class HobbyCategoryViewSet(viewsets.ModelViewSet):    
    serializer_class = HobbyCategorySerializer
    queryset = UserHobbyCategory.objects.all()
    pagination_class=ExamplePagination

I am not sure if there is any easier way for this. this one worked for me. But I think its not good to create new class just to change page_size.

Edit – simple solution is set it like

pagination.PageNumberPagination.page_size = 100 

in ViewSet.

class HobbyCategoryViewSet(viewsets.ModelViewSet):    
    serializer_class = HobbyCategorySerializer
    queryset = UserHobbyCategory.objects.all()
    pagination.PageNumberPagination.page_size = 100 
Answered By: Mangesh

Use page size query params to provide page size dynamically..

from rest_framework.pagination import PageNumberPagination

class StandardResultsSetPagination(PageNumberPagination):
    page_size_query_param = 'limit'

Set Default pagination class in settings

REST_FRAMEWORK = {'DEFAULT_PAGINATION_CLASS': 'StandardResultsSetPagination',}

Now in your URL provide limit as a GET parameter..

http://example.com/list/?limit=100 or 25

Answered By: Naresh

In case you are trying to change the “page size” of the LimitOffsetPagination class, you have to override the default_limit variable instead of page_size:

from rest_framework import paginationclass 


CustomLimitOffsetPagination(pagination.LimitOffsetPagination):
    default_limit = 5
Answered By: oneschilling

In the case that you want to set a default pagination value, including a max and a param, here is what you do.

1) Create a drf_defaults.py (or any name you choose). I placed it in same dir as settings.py:

"""
Django rest framework default pagination
"""
from rest_framework.pagination import PageNumberPagination


class DefaultResultsSetPagination(PageNumberPagination):
    page_size = 50
    page_size_query_param = 'page_size'
    max_page_size = 100000

2) In your settings.py, update REST_FRAMEWORK dict, adding the following:

'DEFAULT_PAGINATION_CLASS': 'drf_defaults.DefaultResultsSetPagination',

In the end my REST_FRAMEWORK settings dict looks like:

# http://www.django-rest-framework.org/api-guide/settings/
REST_FRAMEWORK = {
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [
        # 'rest_framework.permissions.AllowAny',  # Use to disable api auth
        # 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
        'permissions.IsAuthenticatedWriteOrReadOnly',
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        # 'oauth2_provider.contrib.rest_framework.OAuth2Authentication',  # Own oauth server
        'client_authentication.ApiTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ],
    # Enable DRF pagination
    # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'DEFAULT_PAGINATION_CLASS': 'drf_defaults.DefaultResultsSetPagination',
    'PAGE_SIZE': 50,
    'DEFAULT_RENDERER_CLASSES': (
        # 'rest_framework.renderers.JSONRenderer',  # Swapping out the original renderer
        'lib.drf_renderer.UJSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ),
    'DEFAULT_PARSER_CLASSES': (
        # 'rest_framework.parsers.JSONParser',  # Swapping out the original parser
        'lib.drf_parser.UJSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ),
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
    )
}

Your settings will of course vary! Cheers!

Answered By: radtek

Simplest Solution

For LimitOffsetPagination :

  viewsets.ModelViewSet.pagination_class.default_limit = 10 #Page limit

For PageNumberPagination:

    viewsets.ModelViewSet.pagination_class.page_size = 10 #Page limit

For CursorPagination:

viewsets.ModelViewSet.pagination_class.page_size = 10 #Page limit

Full Code:

class HobbyCategoryViewSet(viewsets.ModelViewSet):    
        serializer_class = HobbyCategorySerializer
        queryset = UserHobbyCategory.objects.all()

        viewsets.ModelViewSet.pagination_class.default_limit = 10 #Page limit

Make sure you have DEFAULT_PAGINATION_CLASS in your settings.

Answered By: Sawan Chauhan

use this

from rest_framework.pagination import PageNumberPagination

class StandardResultsSetPagination(PageNumberPagination):
   page_size = 100
   page_size_query_param = 'page_size'

apply the style globally, using the DEFAULT_PAGINATION_CLASS settings key

REST_FRAMEWORK = {
     'DEFAULT_PAGINATION_CLASS': 'your_app*.your_class_file*.StandardResultsSetPagination',
}

example of usage

http://127.0.0.1:8000/produits/api/?page_size=3&page=1
Answered By: Moumen Lahmidi

Anyone experiencing this issue should read this article: http://masnun.rocks/2017/03/20/django-admin-expensive-count-all-queries/

To simplify, there are 2 triggers of the expensive COUNT(*) behavior

  1. the paginator. You need to override it in your ModelAdmin class. Create a class like the one below (for Postgres) or

class LargeTablePaginator(Paginator):

    def _get_count(self):
        if getattr(self, '_count', None) is not None:
            return self._count
        
        query = self.object_list.query
        
        

        if not query.where:
            with connections['default'].cursor() as cursor:
                cursor.execute("SELECT reltuples FROM pg_class WHERE relname = %s",
                            [query.model._meta.db_table])
                self._count = int(cursor.fetchone()[0])
        else:
            self._count = super(LargeTablePaginator, self)._get_count()
            
        return self._count

    count = property(_get_count)

then override it in your ModelAdmin with:

paginator = LargeTablePaginator
  1. In Django >1.8, you need to also add:

    show_full_result_count = False

to your ModelAdmin class.

Performing these 2 steps was the only way I was able to eliminate all count(*) queries.

Answered By: Ben Wilson

I jumped into a similar problem and could not find a proper answer.
Most of the answers change the default page_size and it affects other API views too.

Here I post how I handled it at last.

Define custom pagination and a function returning pagination class.

# pagination.py
from rest_framework import pagination

class CustomPagination(pagination.PageNumberPagination):
    def get_paginated_response(self, data):
        response = super(CustomPagination, self).get_paginated_response(data)
        response.data['total_pages'] = self.page.paginator.num_pages
        return response

def CustomPaginationWithSize(_page_size = 10):
    class CustomPaginationWithSize(pagination.PageNumberPagination):
        page_size = _page_size
        def get_paginated_response(self, data):
            response = super(CustomPaginationWithSize, self).get_paginated_response(data)
            response.data['total_pages'] = self.page.paginator.num_pages
            return response
    return CustomPaginationWithSize

In your settings.py, set the default pagination with the above custom pagination class.

REST_FRAMEWORK = {
#  ...
    'DEFAULT_PAGINATION_CLASS': 'api.pagination.CustomPagination',
    'PAGE_SIZE': 20,
#  ...
}

Now PageNumberPaginationWithCount is applied for all Views with page_size=20 as long as you do not specify pagination_class=None in the View class explicitly.

Finally, for Views that need custom page size, use the function returning custom pagination class as follows.

# sample viewset that needs custom page size 5
class PersonListView(generics.ListAPIView):
    permission_classes = (permissions.IsAuthenticated, )
    serializer_class = PersonSerializer
    pagination_class = CustomPaginationWithSize(5)
    queryset = Person.objects.all()

This does not affect other views and resolves the OP’s problem.

Answered By: David Piao

settings.py

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

these settings will affect every endpoints better to remove both settings from settings.py

views.py

from .pagination import DefaultPagination

class ProductViewSet(ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    pagination_class = DefaultPagination

pagination.py

from rest_framework.pagination import PageNumberPagination

class DefaultPagination(PageNumberPagination):
    page_size = 10
Answered By: Azhar Uddin Sheikh