Skip to main content

Django and Pagination

Django and Pagination

ref

本サイトに使ったfetchするurlを相対的に指定するCursorPaginationのメモです。 次のDjangoのPaginationのうち、データ更新が頻繁なアプリにはカーソル型が適しています。

  • PageNumberPagination e.g. ~/?page=4
  • LimitOffsetPagination e.g. ~/?limit=5&offset=400
  • CursorPagination e.g. ~/?cursor=cj0xJnA9MjAxOC

viewsetに適用する

ListViewsetなどでは、pagination_classを指定するだけで完了します。 今回はテンプレートを利用していないため、自分でページネーションを適用します。 後半の関数引数requestは、各クラスにrequestを与えるためあとでオーバーライドさせます。

class CustomViewSet (GenericViewSet):
queryset = CustomModel.objects.all() # CustomModelは別で定義
serializer_class = CustomSerializer # CustomSerializerは後で定義
pagination_class = CustomPagination # CustomPaginationは後で定義
def list (self, request):
queryset = self.filter_queryset( self.get_queryset() )
paginate = self.paginate_qeryset(objs)
if paginate is None:
return self.get_paginated_response(None , request=request)
data = self.get_serializer(paginate, many=True, request=request)
return self.get_paginated_response(data.data , request=request)

ViewsetのResponseを拡張する

GenericViewSetで定義されているself.get_paginated_responseのソースコードを確認すると、 self.paginatorの値を参照し,Dictを返しているだけなので、簡単にオーバーライドできます。 第二引数にrequestを指定することで、アクセスしたユーザー情報によってResponseを指定できます。

class CustomViewSet (GenericViewSet):
...
def get_paginated_response(self, data, request=None):
return Response({
'next' : self.paginator.get_next_link() if data else None,
'previous': self.paginator.get_previous_link() if data else None,
'results' : data if data else "Page Not found.",
'isAuth' : True if request.user else False,
}, status = 404 if data is None else 200)

Serializerを拡張する

ユーザーの情報によって、シリアライズする値を変更します。 例えば、アクセスしたユーザーがそのデータの著者かどうかを判定します。

from rest_framework import serializers as s

class NoteSerializer(s.ModelSerializer):
...
is_author = s.SerializerMethodField()
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super().__init__()
def get_is_author(self, obj):
user = self.request and self.request.user
return user and user.id == obj.posted_user.id

paginationをカスタムする

self.ordering = yyyとViewSetの途中のコードからpaginationの値を変更させるため、 各パラメータのget_xxxのコード (例えばorderingだと、get_ordering)をつぎのようにオーバーライドします。

class CustomViewSet (GenericViewSet):
ordering = "-id"
...
def ...
self.ordering = "id"

class CustomPagination(CursorPagination):
page_size = 5
max_page_size = 5
cursor_query_param = 'cursor'
invalid_cursor_message = 'Invalid cursor(;_;)'
def get_ordering(self, request, queryset, view):
view_ordering = getattr(view, 'ordering', None)
if view_ordering:
self.ordering = view_ordering
return super(NotePagination, self).get_ordering(request, queryset, view)