一、ListModelMixin 和GenericAPIView源碼
ListModelMixin 是一個單一功能類,必須配合GenericAPIView(或其子類)來一起使用,才能完成其視圖的功能
class ListModelMixin:"""List a queryset."""def list(self, request, *args, **kwargs):#1、這里獲取querysetqueryset = self.filter_queryset(self.get_queryset())#2、這里獲取當前頁的queryset數據,[<數據模型>]page = self.paginate_queryset(queryset)if page is not None:#3、對當前頁的模型對象,進行序列化serializer = self.get_serializer(page, many=True)#4、獲取到當前頁的數據,里面整合了當前頁的序列化數據return self.get_paginated_response(serializer.data)serializer = self.get_serializer(queryset, many=True)return Response(serializer.data)
主要就按照:一步一步走就可以了,如果ListModelMixin沒有的方法,就去GenericAPIView找
self.filter_queryset(self.get_queryset())--->self.paginate_queryset(queryset)-->self.get_serializer(page, many=True)-->self.get_paginated_response(serializer.data)
1、ListModelMixin : queryset = self.filter_queryset(self.get_queryset())
當前類是沒有實現get_queryset和filter_queryset方法的,
配合GenericAPIView使用,調用的是GenericAPIView的get_queryset和filter_queryset方法
2、GenericAPIView:? get_queryset, 拿到queryset對象
def get_queryset(self):queryset = self.querysetif isinstance(queryset, QuerySet):queryset = queryset.all()return queryset
3、GenericAPIView:filter_queryset 這個是過濾器,沒有配置就是空,沒有做如何操作
4、GenericAPIView:paginate_queryset(queryset) :
4.1、GenericPAIView : paginator : 拿到配置的分頁器類,返回分頁器的實例化對象
@property
def paginator(self):if not hasattr(self, '_paginator'):if self.pagination_class is None:self._paginator = Noneelse:self._paginator = self.pagination_class()return self._paginator
4.2、根據4.1,拿到的分頁器對象,調用分頁器的paginate_queryset 方法: 該方法就是返回當前頁的queryset
def paginate_queryset(self, queryset):if self.paginator is None:return Nonereturn self.paginator.paginate_queryset(queryset, self.request, view=self)
5、GenericAPIView: get_paginated_response 方法: 調用分頁器對象的get_paginated_response
def get_paginated_response(self, data):assert self.paginator is not Nonereturn self.paginator.get_paginated_response(data)
總結來說:
1、要滿足第一個要求,符合ListModelMixin的功能,需要在自定義的分頁器類中重寫paginate_queryset和get_paginated_response
2、在start方法中,調用這兩個方法
3、再開一個方法handle_data,在get_paginated_response中調用這個方法,后續重寫handle_data來進行數據定制功能
二、自定義PageNumber的分頁器
(1)自定義的基本分頁器類
from rest_framework.pagination import PageNumberPaginationclass GenericPageNumberPagination(PageNumberPagination):page_size = 20page_query_param = 'page'page_size_query_param = 'page_size'max_page_size = page_size+(page_size//2)#重寫父類的: 校驗page_sizedef get_page_size(self, request):'''功能:校驗page_size,沒有傳遞或傳遞類型有問題時,按照默認的大小設置,超過最大頁面大小時,設置成最大頁面:param request::return: 數值'''page_size = request.GET.get(self.page_size_query_param)if not page_size:page_size = self.page_size #取的是類的page_sizeelse:try:page_size = int(page_size)except Exception:page_size = self.page_size #取的是類的page_sizeif page_size > self.max_page_size:page_size = self.max_page_size#給分頁的實例對象賦值每頁大小self.page_size = page_sizereturn self.page_size#自己定義: 校驗pagedef get_page(self,request,queryset,page_size):'''功能:判斷攜帶page是否合法,頁碼必須大于0,不能大于最大頁數,頁碼必須是數值:param request: 當前請求對象:param queryset: 模型uqeryset:param page_size: 每頁大小:return: None,{"error":'錯誤消息'},數值'''page = request.GET.get(self.page_query_param)if page == None:return Nonetry:page = int(page)self.page = page#判斷查詢的頁碼是否大于0if page <=0:self.page = {'error':'頁碼必須大于0','code':400}#總數據量self.count = len(queryset)##判斷查詢的數據是否有數據if self.count == 0:self.page = {'error':'查詢不到相關數據','code':400}#總頁數self.pages, has_number = divmod(self.count,page_size)if has_number:self.pages += 1## 判斷查詢的頁碼是否大于總頁碼if page > self.pages:self.page = {'error':f'查詢的頁碼{page}大于總頁碼數{self.pages}','code':400}return self.pageexcept Exception:self.page = {'error':'頁碼必須是數值','code':400}return self.page#自定義的def has_next_page(self,page=None,pages = None):'''是否有下一頁的數據:param page: 當前頁:param pages: 總頁數:return: 0或1'''if page == None:page = self.pageif pages == None:pages = self.pagesif page == 1:if pages <= 1:return 0else:return 1else:#page > 1if pages > page:return 1else:return 0#自定義的def has_previous_page(self,page=None,pages = None):'''功能:是否有上一頁的數據:param page: 當前頁:param pages: 總頁數:return: 0或1'''if page == None:page = self.pageif pages == None:pages = self.pagesif pages <=1:return 0else:if page>1:return 1else:return 0#重寫父類的: 獲取當前頁的QuerySet對象def paginate_queryset(self, queryset, request, view=None):'''功能:獲取當前頁的queryset對象:param queryset: 所有的queryset:param request: 當前請求:param view: 當前視圖類:return: None,{’error‘:''}, QuerySet'''page_size = self.get_page_size(request)page = self.get_page(request,queryset,page_size)if isinstance(page,dict) or page == None:#page參數錯誤,沒有傳遞page時return pagequeryset_len = len(queryset)if queryset_len == 0:return {'error':'查詢不到相關數據','code':400}#1、總數據量,在get_page 中就設置了self.count = len(queryset)#2、總頁數,在get_page 中就設置了self.pages, has_number = divmod(self.count, page_size)if has_number:self.pages += 1# 3、下一頁self.next = self.has_next_page() # 有下一頁,返回1,沒有返回0# 4、上一頁self.previous = self.has_next_page() # 有上一頁時,返回1,沒有返回0#5、當前頁self.current_page = self.page#6、截取指定的數據if self.page == 1:self.queryset = queryset[:self.page_size]else:start = self.page_size*(self.page-1)end = self.page_size*(self.page)self.queryset = queryset[start:end]return self.queryset#重寫父類的:將當前頁的QuerySet對象序列化def get_paginated_response(self,page_queryset,serializer_class=None):'''功能:根據傳遞進來的dict,None(page沒有傳遞,就不返回數據了),當前頁的List,當前頁QuerySet,:param page_queryset: dict,None,List,QuerySet:param serializer_class: 序列化器類:return: {}'''if isinstance(page_queryset,dict):#paginate_queryset的錯誤消息return page_querysetelif isinstance(page_queryset,list):data = {'code': 201,'msg': '獲取數據成功','data': '', # 當前頁數據'next': self.has_next_page(), # 是否有下一頁'previous': self.has_previous_page(), # 是否有上一頁'count': self.count, # 總數據量'pages': self.pages, # 總頁碼數'current_page': self.page, # 當前頁碼'page_size': self.page_size, # 每頁大小}if serializer_class:#1、手動使用start方法調用時,page_ser = serializer_class(instance=page_queryset, many=True)page_data = page_ser.datadata['data'] = page_datareturn dataelse:#2、配合mixins.ListModelMixin源碼使用, 其調用了paginate_queryset,拿到queryset對象,將序列化結果傳遞進來page_queryset = self.handle_data(page_queryset)data['data'] = page_querysetreturn dataelif page_queryset == None:return {'code':400,'error':'沒有傳遞頁碼值'}else:raise Exception('分頁器只支持,dict,list,QuerySet,None 類型')#自己定義的,對當前頁數據,進一步的處理def handle_data(self,data_list):return data_list#手動調用時,獲取結果的def start(self,request,serializer_class,queryset):#1、獲取當前頁的QuerySet對象page_queryset = self.paginate_queryset(queryset=queryset,request=request)if page_queryset == None:return {'code':400,'error':'頁碼必須攜帶'}if isinstance(page_queryset,dict):return page_queryset#2、將當前頁的QuerySet對象傳遞進去,得到分頁的響應結果page_data = self.get_paginated_response(page_queryset=page_queryset,serializer_class=serializer_class)return page_dataif __name__ == '__main__':'''mixins.ListModelMixin的調用邏輯:1、調用paginate_queryset2、1的返回值如果是None時,就不執行獲取分頁的數據,就去獲取所有的數據返回3、1的返回值不為None時,就執行get_paginated_response,拿到分頁的結果手動調用使用:data = page.start(request,序列化類,模型對象)'''
(2)、使用
1、對于ListModelMixin,使用的邏輯是一樣的,無需變化。
2、手動使用
page = GenericPageNumberPagination()
page_data = page.start(request,'序列化器類','數據庫查詢的queryset')
(3)、繼承
對于需要特殊處理返回的數據時,可以繼承分頁器類,重寫handle_data方法
class UserPageNumberPagination(GenericPageNumberPagination):def handle_data(self,data_list):for dic in data_list:dic['type'] = '患者'return data_list#使用
page = UserPageNumberPagination()
page_data = page.start(request,'序列化器類','數據庫查詢到關于用戶的queryset')