Django and Pagination
本サイトに使ったfetchするurlを相対的に指定するCursorPaginationのメモです。 次のDjangoのPaginationのうち、データ更新が頻繁なアプリにはカーソル型が適しています。
- PageNumberPagination
e.g. ~/?page=4
- LimitOffsetPagination
e.g. ~/?limit=5&offset=400
- CursorPagination
e.g. ~/?cursor=cj0xJnA9MjAxOC
ref
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)