一.mixins泛型视图类

一般自定义视图类在使用mixins泛型类时也就是继承该类,还需要再继承一个GenericAPIView类搭配使用

1.1mixins源码介绍:

DRF框架的介绍与理解 - 图1

1.1.1 CreateModelMixin源码

  1. class CreateModelMixin:
  2. """
  3. Create a model instance.
  4. """
  5. def create(self, request, *args, **kwargs):
  6. #get_serializer方法得到属性序列化类serializer_class()视图函数中你需要指定该属性
  7. #serializer_class=你自定义的序列化类,这里没有使用many参数,不用一般默认False,
  8. #也就是适合单对象的序列化
  9. serializer = self.get_serializer(data=request.data)
  10. #raise_exception=True验证当有异常时抛出异常
  11. serializer.is_valid(raise_exception=True)
  12. #调用下方perform_create方法进行保存对象
  13. self.perform_create(serializer)
  14. headers = self.get_success_headers(serializer.data)
  15. return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
  16. def perform_create(self, serializer):
  17. serializer.save()
  18. def get_success_headers(self, data):
  19. try:
  20. return {'Location': str(data[api_settings.URL_FIELD_NAME])}
  21. except (TypeError, KeyError):
  22. return {}

1.1.2 ListModelMixin源码

  1. class ListModelMixin:
  2. """
  3. List a queryset.
  4. """
  5. def list(self, request, *args, **kwargs):
  6. #get_queryset在GenericAPIView类,返回的是GenericAPIView视图类中queryset属性
  7. #filter_queryset()得到的是后端过滤后的查询集(搭配过滤器使用)后面会讲
  8. queryset = self.filter_queryset(self.get_queryset())
  9. #paginate_queryset返回结果的单个页面,如果分页被禁用,则返回“None”。
  10. page = self.paginate_queryset(queryset)
  11. if page is not None:
  12. serializer = self.get_serializer(page, many=True)
  13. return self.get_paginated_response(serializer.data)
  14. serializer = self.get_serializer(queryset, many=True)
  15. return Response(serializer.data)

1.1.3 RetrieveModelMixin源码

  1. class RetrieveModelMixin:
  2. """
  3. Retrieve a model instance.
  4. """
  5. #该方法使用场景为url位置参数:类似于url(r'^books/(?P<pk>\d+)/$'
  6. def retrieve(self, request, *args, **kwargs):
  7. #GenericAPIView类中的get_object通过url位置参数返回一个单对象
  8. instance = self.get_object()
  9. serializer = self.get_serializer(instance)
  10. return Response(serializer.data)

1.1.4 UpdateModelMixin源码

  1. class UpdateModelMixin:
  2. """
  3. 单实例更新:
  4. 1. 获取 kwargs 字典中 partial 的值,如果没有,则返回False,有,则返回值
  5. 2. 获取单个对象
  6. 3. 通过序列化,获取单个对象的序列化对象
  7. 4. 判断序列化对象的有效性
  8. 5. 更新数据
  9. """
  10. def update(self, request, *args, **kwargs):
  11. partial = kwargs.pop('partial', False)
  12. instance = self.get_object()
  13. serializer = self.get_serializer(instance, data=request.data, partial=partial)
  14. serializer.is_valid(raise_exception=True)
  15. self.perform_update(serializer)
  16. if getattr(instance, '_prefetched_objects_cache', None):
  17. # If 'prefetch_related' has been applied to a queryset, we need to
  18. # forcibly invalidate the prefetch cache on the instance.
  19. instance._prefetched_objects_cache = {}
  20. return Response(serializer.data)
  21. def perform_update(self, serializer):
  22. serializer.save()
  23. def partial_update(self, request, *args, **kwargs):
  24. #这里给了一个partial为Ture,然后调用update()
  25. kwargs['partial'] = True
  26. return self.update(request, *args, **kwargs)

1.1.4 DestroyModelMixin源码

  1. class DestroyModelMixin:
  2. """
  3. 删除单对象数据
  4. """
  5. def destroy(self, request, *args, **kwargs):
  6. instance = self.get_object()
  7. self.perform_destroy(instance)
  8. return Response(status=status.HTTP_204_NO_CONTENT)
  9. def perform_destroy(self, instance):
  10. instance.delete()

1.2 视图集viewset的源码:

  1. class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
  2. """
  3. 默认情况下,GenericViewSet类不提供任何操作,
  4. 但包含通用视图行为的基本集,如
  5. ' get_object '和' get_queryset '方法。
  6. """
  7. pass
  8. class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
  9. mixins.ListModelMixin,
  10. GenericViewSet):
  11. """
  12. 提供默认的' list() '和' retrieve() '动作的视图集.
  13. """
  14. pass
  15. class ModelViewSet(mixins.CreateModelMixin,
  16. mixins.RetrieveModelMixin,
  17. mixins.UpdateModelMixin,
  18. mixins.DestroyModelMixin,
  19. mixins.ListModelMixin,
  20. GenericViewSet):
  21. """
  22. 一个视图集提供了默认的' create() ', ' retrieve() ', ' update() ',
  23. ' partial_update() ', ' destroy() '和' list() '操作。
  24. """
  25. pass

1.3 generic相关视图的介绍:

1.3.1 视图架构图

DRF框架的介绍与理解 - 图2

1.3.1 视图源码

  1. class CreateAPIView(mixins.CreateModelMixin,
  2. GenericAPIView):
  3. """
  4. Concrete view for creating a model instance.
  5. """
  6. def post(self, request, *args, **kwargs):
  7. return self.create(request, *args, **kwargs)
  8. class ListAPIView(mixins.ListModelMixin,
  9. GenericAPIView):
  10. """
  11. Concrete view for listing a queryset.
  12. """
  13. def get(self, request, *args, **kwargs):
  14. return self.list(request, *args, **kwargs)
  15. class RetrieveAPIView(mixins.RetrieveModelMixin,
  16. GenericAPIView):
  17. """
  18. Concrete view for retrieving a model instance.
  19. """
  20. def get(self, request, *args, **kwargs):
  21. return self.retrieve(request, *args, **kwargs)
  22. class DestroyAPIView(mixins.DestroyModelMixin,
  23. GenericAPIView):
  24. """
  25. Concrete view for deleting a model instance.
  26. """
  27. def delete(self, request, *args, **kwargs):
  28. return self.destroy(request, *args, **kwargs)
  29. class UpdateAPIView(mixins.UpdateModelMixin,
  30. GenericAPIView):
  31. """
  32. Concrete view for updating a model instance.
  33. """
  34. def put(self, request, *args, **kwargs):
  35. return self.update(request, *args, **kwargs)
  36. def patch(self, request, *args, **kwargs):
  37. return self.partial_update(request, *args, **kwargs)
  38. class ListCreateAPIView(mixins.ListModelMixin,
  39. mixins.CreateModelMixin,
  40. GenericAPIView):
  41. """
  42. Concrete view for listing a queryset or creating a model instance.
  43. """
  44. def get(self, request, *args, **kwargs):
  45. return self.list(request, *args, **kwargs)
  46. def post(self, request, *args, **kwargs):
  47. return self.create(request, *args, **kwargs)
  48. class RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
  49. mixins.UpdateModelMixin,
  50. GenericAPIView):
  51. """
  52. Concrete view for retrieving, updating a model instance.
  53. """
  54. def get(self, request, *args, **kwargs):
  55. return self.retrieve(request, *args, **kwargs)
  56. def put(self, request, *args, **kwargs):
  57. return self.update(request, *args, **kwargs)
  58. def patch(self, request, *args, **kwargs):
  59. return self.partial_update(request, *args, **kwargs)
  60. class RetrieveDestroyAPIView(mixins.RetrieveModelMixin,
  61. mixins.DestroyModelMixin,
  62. GenericAPIView):
  63. """
  64. Concrete view for retrieving or deleting a model instance.
  65. """
  66. def get(self, request, *args, **kwargs):
  67. return self.retrieve(request, *args, **kwargs)
  68. def delete(self, request, *args, **kwargs):
  69. return self.destroy(request, *args, **kwargs)
  70. class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
  71. mixins.UpdateModelMixin,
  72. mixins.DestroyModelMixin,
  73. GenericAPIView):
  74. """
  75. Concrete view for retrieving, updating or deleting a model instance.
  76. """
  77. def get(self, request, *args, **kwargs):
  78. return self.retrieve(request, *args, **kwargs)
  79. def put(self, request, *args, **kwargs):
  80. return self.update(request, *args, **kwargs)
  81. def patch(self, request, *args, **kwargs):
  82. return self.partial_update(request, *args, **kwargs)
  83. def delete(self, request, *args, **kwargs):
  84. return self.destroy(request, *args, **kwargs)

二.视图中所涉及的重要的源码

2.1 GenericAPIView:

  1. class GenericAPIView(views.APIView):
  2. """
  3. Base class for all other generic views.
  4. """
  5. #你需要设置这些属性,
  6. #或重写' get_queryset() ' / ' get_serializer_class() '。
  7. #如果你重写一个视图方法,它是重要的调用
  8. # ' get_queryset() '而不是直接访问' queryset '属性,
  9. # as ' queryset '只会被求值一次,并且这些结果会被缓存
  10. #对于所有后续请求。
  11. queryset = None
  12. serializer_class = None
  13. #如果你想使用对象查找而不是pk,设置'lookup_field'。
  14. #对于更复杂的查找需求,需重写' get_object() '。
  15. lookup_field = 'pk' #默认为pk也就是主键
  16. lookup_url_kwarg = None #url中的位置参数
  17. #用于查询集过滤的过滤后端类(往往这里常用的就是结合django的settings中配置REST_FRAMEWORK = {
  18. #'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']},属于
  19. #全局变量,你也可以不选择全局配置,在自己的视图集类下给该属性赋值:filter_backends = [DjangoFi
  20. #lterBackend]
  21. filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
  22. # 用于查询集分页的样式。
  23. pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
  24. def get_queryset(self):
  25. """
  26. 获取此视图的项目列表。
  27. 这必须是一个可迭代对象,并且是一个查询集。
  28. 默认使用' self.queryset '。
  29. 应该一直使用这个方法而不是访问self.queryset
  30. 直接self.queryset '只求值一次,这些结果
  31. 为所有后续请求缓存。
  32. 如果需要提供不同的服务,您可能希望重写此选项
  33. 查询集,取决于传入的请求。
  34. (如。返回特定于用户的项目列表)
  35. """
  36. assert self.queryset is not None, (
  37. "'%s' should either include a `queryset` attribute, "
  38. "or override the `get_queryset()` method."
  39. % self.__class__.__name__
  40. )
  41. queryset = self.queryset
  42. if isinstance(queryset, QuerySet):
  43. # 确保对每个请求重新评估queryset.
  44. queryset = queryset.all()
  45. return queryset
  46. def get_object(self):
  47. """
  48. 返回视图显示的对象。
  49. 如果您需要提供非标准的,您可能需要重写它
  50. queryset查找。例如,如果对象使用multiple引用
  51. url conf中的关键字参数。
  52. """
  53. #得到过滤后查询集赋值给queryset
  54. queryset = self.filter_queryset(self.get_queryset())
  55. # Perform the lookup filtering.
  56. lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
  57. #断言位置参数有没有在kwargs
  58. assert lookup_url_kwarg in self.kwargs, (
  59. 'Expected view %s to be called with a URL keyword argument '
  60. 'named "%s". Fix your URL conf, or set the `.lookup_field` '
  61. 'attribute on the view correctly.' %
  62. (self.__class__.__name__, lookup_url_kwarg)
  63. )
  64. filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
  65. obj = get_object_or_404(queryset, **filter_kwargs)
  66. # 权限的验证函数
  67. self.check_object_permissions(self.request, obj)
  68. return obj
  69. def get_serializer(self, *args, **kwargs):
  70. """
  71. 返回应该用于验证和的序列化器实例,反序列化输入和序列化输出。
  72. 例如如果想要序列化list多对象的创建入库,可以重写该方法,在其中进行request.data
  73. 为列表时,返回serializer_class(many=True, *args, **kwargs)加入many参数
  74. """
  75. #从下方get_serializer_class看出最后得到了self.serializer_class
  76. #相当于serializer_class=self.serializer_class,把类属性serializer_class赋给了serializer_class
  77. serializer_class = self.get_serializer_class()
  78. #kwargs设置默认值,也就是需要序列化的额外内容context
  79. kwargs.setdefault('context', self.get_serializer_context())
  80. #大家可以看到此时返回的序列化类的实例
  81. return serializer_class(*args, **kwargs)
  82. def get_serializer_class(self):
  83. """
  84. 断言不为空时返回序列化类名,反之就返回类型应该包含serializer_class属性
  85. 或者重写get_serializer_class方法的异常字样
  86. """
  87. assert self.serializer_class is not None, (
  88. "'%s' should either include a `serializer_class` attribute, "
  89. "or override the `get_serializer_class()` method."
  90. % self.__class__.__name__
  91. )
  92. return self.serializer_class
  93. def get_serializer_context(self):
  94. """
  95. Extra context provided to the serializer class.
  96. 序列化类提供额外的序列参数context上下文
  97. """
  98. return {
  99. 'request': self.request,
  100. 'format': self.format_kwarg,
  101. 'view': self
  102. }
  103. def filter_queryset(self, queryset):
  104. '''
  105. 给定一个查询集,使用过滤后端类及其下方法过滤它。返回得到过滤后的查询集,如filter_backends
  106. 指定的是DjangoFilterBackend,其实它调用了原本属于源码DjangoFilterBackend类下的filter_qu
  107. eryset,其中backend代表我自定义的filterset类,filter_backends属性代表我们自定义的filter
  108. set类的列表,然后使用backend()也就是自定义的过滤集类的对象调用filter_queryset,返回过滤后
  109. 的查询集赋值给了queryset
  110. '''
  111. for backend in list(self.filter_backends):
  112. queryset = backend().filter_queryset(self.request, queryset, self)
  113. return queryset
  114. #此装饰器的作用就是将函数转变成属性
  115. @property
  116. def paginator(self):
  117. """
  118. 返回与视图关联的分页器实例,或者“None”。
  119. """
  120. if not hasattr(self, '_paginator'):
  121. if self.pagination_class is None:
  122. self._paginator = None
  123. else:
  124. self._paginator = self.pagination_class()
  125. return self._paginator
  126. def paginate_queryset(self, queryset):
  127. """
  128. 返回结果的单个页面,如果分页被禁用,则返回“None”。
  129. 其中的self.paginator代表的是分页类的实例,然后调用分页类源码下paginate_queryset()
  130. 返回的是页面对象的列表
  131. """
  132. if self.paginator is None:
  133. return None
  134. return self.paginator.paginate_queryset(queryset, self.request, view=self)
  135. def get_paginated_response(self, data):
  136. """
  137. 返回给定输出数据的分页样式' Response '对象。
  138. """
  139. assert self.paginator is not None
  140. return self.paginator.get_paginated_response(data)

2.3 PageNumberPagination:

  1. class PageNumberPagination(BasePagination):
  2. """
  3. 一个简单的基于页码的样式,它支持如下的页码
  4. 查询参数。例如:
  5. http://api.example.org/accounts/?page=4
  6. http://api.example.org/accounts/?page=4&page_size=100
  7. """
  8. # The default page size.
  9. # 默认为 `None`, 意思是分页被禁用.
  10. page_size = api_settings.PAGE_SIZE
  11. #django_paginator_class赋值了Django 分页器类(DjangoPaginator)
  12. django_paginator_class = DjangoPaginator
  13. # 客户端可以使用此查询参数控制页面.
  14. page_query_param = 'page'
  15. page_query_description = _('A page number within the paginated result set.')
  16. #客户端可以使用这个查询参数来控制页面大小。
  17. #默认为“None”。设置为eg 'page_size'以启用使用。
  18. page_size_query_param = None
  19. #每页返回的结果数
  20. page_size_query_description = _('Number of results to return per page.')
  21. #设置一个整数来限制客户端可能请求的最大页面大小。
  22. #只有当设置了'page_size_query_param'时才相关.
  23. max_page_size = None
  24. #字符串值的列表或元组,指可与请求集合中最后一页一起使用的值。默认为page_query_param('last',)
  25. last_page_strings = ('last',)
  26. #在可浏览 API 中呈现分页控件时要使用的模板的名称。可以重写以修改呈现样式,
  27. #或设置为完全禁用 HTML 分页控件。
  28. template = 'rest_framework/pagination/numbers.html'
  29. invalid_page_message = _('Invalid page.')
  30. #该方法将传递初始查询集参数,并应返回一个可迭代对象。该对象仅包含所请求页面(本页面)中的数据
  31. def paginate_queryset(self, queryset, request, view=None):
  32. """
  33. 如果需要,对查询集进行分页,或者返回一个页面对象
  34. 如果没有为此视图配置分页,则为' None '。
  35. """
  36. #获取页面的大小
  37. page_size = self.get_page_size(request)
  38. if not page_size: #如果不存在返回为none
  39. return None
  40. paginator = self.django_paginator_class(queryset, page_size)
  41. page_number = self.get_page_number(request, paginator)
  42. try:
  43. self.page = paginator.page(page_number)
  44. except InvalidPage as exc:
  45. msg = self.invalid_page_message.format(
  46. page_number=page_number, message=str(exc)
  47. )
  48. raise NotFound(msg)
  49. if paginator.num_pages > 1 and self.template is not None:
  50. # The browsable API should display pagination controls.
  51. self.display_page_controls = True
  52. self.request = request
  53. return list(self.page)
  54. #返回分页的页数
  55. def get_page_number(self, request, paginator):
  56. page_number = request.query_params.get(self.page_query_param, 1)
  57. if page_number in self.last_page_strings:
  58. page_number = paginator.num_pages
  59. return page_number
  60. #该方法将传递一个序列化的页面数据,并应返回一个实例
  61. def get_paginated_response(self, data):
  62. return Response(OrderedDict([
  63. ('count', self.page.paginator.count),
  64. ('next', self.get_next_link()),
  65. ('previous', self.get_previous_link()),
  66. ('results', data)
  67. ]))
  68. #返回一个页面显示的模式样板
  69. def get_paginated_response_schema(self, schema):
  70. return {
  71. 'type': 'object',
  72. 'properties': {
  73. 'count': {
  74. 'type': 'integer',
  75. 'example': 123,
  76. },
  77. 'next': {
  78. 'type': 'string',
  79. 'nullable': True,
  80. 'format': 'uri',
  81. 'example': 'http://api.example.org/accounts/?{page_query_param}=4'.format(
  82. page_query_param=self.page_query_param)
  83. },
  84. 'previous': {
  85. 'type': 'string',
  86. 'nullable': True,
  87. 'format': 'uri',
  88. 'example': 'http://api.example.org/accounts/?{page_query_param}=2'.format(
  89. page_query_param=self.page_query_param)
  90. },
  91. 'results': schema,
  92. },
  93. }
  94. #获取页面的容量大小
  95. def get_page_size(self, request):
  96. if self.page_size_query_param:
  97. try:
  98. return _positive_int(
  99. request.query_params[self.page_size_query_param],
  100. strict=True,
  101. cutoff=self.max_page_size
  102. )
  103. except (KeyError, ValueError):
  104. pass
  105. return self.page_size
  106. #在结果中返回下一个页面链接地址
  107. def get_next_link(self):
  108. if not self.page.has_next():
  109. return None
  110. url = self.request.build_absolute_uri()
  111. page_number = self.page.next_page_number()
  112. return replace_query_param(url, self.page_query_param, page_number)
  113. #在结果中返回上一个页面链接地址
  114. def get_previous_link(self):
  115. if not self.page.has_previous():
  116. return None
  117. url = self.request.build_absolute_uri()
  118. page_number = self.page.previous_page_number()
  119. if page_number == 1:
  120. return remove_query_param(url, self.page_query_param)
  121. return replace_query_param(url, self.page_query_param, page_number)

2.3 Paginator:

提供参考的第三方举例详情地址https://www.jianshu.com/p/e9686192e7ee

  1. class Paginator:
  2. # Translators: 字符串,用于替换省略页中的省略页码
  3. # 分页器生成的范围, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10].
  4. ELLIPSIS = _('…')
  5. def __init__(self, object_list, per_page, orphans=0,
  6. allow_empty_first_page=True):
  7. self.object_list = object_list
  8. self._check_object_list_is_ordered()
  9. self.per_page = int(per_page) #每页中允许的最大对象数。必选参数
  10. self.orphans = int(orphans) #这是一个缺省参数,如果最后一页的数据小于这个值,会合并到上一页
  11. self.allow_empty_first_page = allow_empty_first_page #允许首页为空 ,默认为True
  12. def __iter__(self):
  13. for page_number in self.page_range:
  14. yield self.page(page_number)
  15. def validate_number(self, number):
  16. """校验给的基于1的页数"""
  17. try:
  18. #先判断页面数是不是float并且不是整形抛出异常
  19. if isinstance(number, float) and not number.is_integer():
  20. raise ValueError
  21. number = int(number)
  22. except (TypeError, ValueError):
  23. raise PageNotAnInteger(_('That page number is not an integer'))
  24. #如果页数小于1,抛出EmptyPage
  25. if number < 1:
  26. raise EmptyPage(_('That page number is less than 1')
  27. #如果页数大于总页数,抛出EmptyPage
  28. if number > self.num_pages:
  29. #这个就是属于结果为0的情况没有一个页,并且允许首页为空就pass,否则抛出空页面异常
  30. if number == 1 and self.allow_empty_first_page:
  31. pass
  32. else:
  33. raise EmptyPage(_('That page contains no results'))
  34. return number
  35. def get_page(self, number):
  36. """
  37. 返回指定页的page对象,页码从1开始。该方法可处理超出范围或无效的页码
  38. 如果给定页码不是数字则返回第一页,如果给定页码小于1或大于总页数,则返回最后一页
  39. """
  40. try:
  41. #先得到一个通过验证的页数值
  42. number = self.validate_number(number)
  43. #给出不是整形的异常,则给number赋值1
  44. except PageNotAnInteger:
  45. number = 1
  46. #给出空页异常处理,则给number赋值为总页数也就是最后一页
  47. except EmptyPage:
  48. number = self.num_pages
  49. return self.page(number)
  50. def page(self, number):
  51. """返回指定页的page对象,它不能处理超出范围或无效页码,指定的页码无效时会触发validate_number异常."""
  52. number = self.validate_number(number)
  53. #bottom值代表的是指定页中的第一个对象在self.object_list列表中的下标
  54. #不代表该页面第一个对象所排序的位置,例如一共5个对象,一个页面容量2个对象,总共就是3个页面
  55. #第二个页面的第一个也就是起始对象的序号是3
  56. bottom = (number - 1) * self.per_page
  57. #top值代表的self.object_list列表中的区间的右边界值(但不包含)其实取的也就是该页的最后一个对象
  58. top = bottom + self.per_page
  59. #top + self.orphans(代表列表右边界值)如果大于等于所有页的总对象数,两边同时家去top值,相当于
  60. #最后一页的对象数量小于等于self.orphans值,则将最后一页的对象数合并到前一页中
  61. if top + self.orphans >= self.count:
  62. top = self.count
  63. #object_list[bottom:top]代表该页的所有对象
  64. return self._get_page(self.object_list[bottom:top], number, self)
  65. def _get_page(self, *args, **kwargs):
  66. """
  67. 返回单个页面的实例。
  68. 其中的Page是另一个源码类
  69. """
  70. return Page(*args, **kwargs)
  71. @cached_property #缓存属性的意思,类似于@property的效果
  72. def count(self):
  73. """返回所有页面中的对象总数"""
  74. c = getattr(self.object_list, 'count', None)
  75. if callable(c) and not inspect.isbuiltin(c) and method_has_no_args(c):
  76. return c()
  77. return len(self.object_list)
  78. @cached_property
  79. def num_pages(self):
  80. """返回总页数."""
  81. #如果总对象数=0,并且self.allow_empty_first_page不为True,返回0
  82. if self.count == 0 and not self.allow_empty_first_page:
  83. return 0
  84. #max取最大值,其中self.count - self.orphans可能会看不懂为什么这么减,但是你只要验证符合页面数
  85. #计算结果,例如count为5,orphans=2,差值hits就是3,那hits/self.per_page=1.5,你会发现页面数为2
  86. #是符合原本应该为3页其最后一页为1个对象满足小于等于orphans值2,也就符合合并到前一页,成为2页。
  87. hits = max(1, self.count - self.orphans)
  88. #以整数形式返回该变量的上限值,如ceil(1.5)=2。
  89. return ceil(hits / self.per_page)
  90. @property
  91. def page_range(self):
  92. """
  93. 返回页面迭代器,页码从1开始
  94. """
  95. #self.num_pages+1为右边界值(开区间),为的是能取到最后一页的值
  96. return range(1, self.num_pages + 1)
  97. def _check_object_list_is_ordered(self):
  98. """
  99. 警告实例。object_list是无序的(通常是QuerySet)。
  100. """
  101. ordered = getattr(self.object_list, 'ordered', None)
  102. if ordered is not None and not ordered:
  103. obj_list_repr = (
  104. '{} {}'.format(self.object_list.model, self.object_list.__class__.__name__)
  105. if hasattr(self.object_list, 'model')
  106. else '{!r}'.format(self.object_list)
  107. )
  108. warnings.warn(
  109. 'Pagination may yield inconsistent results with an unordered '
  110. 'object_list: {}.'.format(obj_list_repr),
  111. UnorderedObjectListWarning,
  112. stacklevel=3
  113. )
  114. def get_elided_page_range(self, number=1, *, on_each_side=3, on_ends=2):
  115. """
  116. 返回一个基于1的页面范围,其中某些值被省略。
  117. 第一个参数number为当前页码数, on_each_side为当前页码左右两边的页数,on_ends为首尾页码范围。
  118. 如果当前页码为第10页,使用该方法将输出如下页码范围(page_range)。10左右两边各有3页,首尾各
  119. 有2页,其余页码号码用...代替。将page_range这个变量传递到前端模板进行遍历即可实现智能分页。
  120. 例如:[1, 2, '…', 7, 8, 9, 10, 11, 12, 13, '…', 49, 50]
  121. """
  122. number = self.validate_number(number)
  123. if self.num_pages <= (on_each_side + on_ends) * 2:
  124. yield from self.page_range
  125. return
  126. if number > (1 + on_each_side + on_ends) + 1:
  127. yield from range(1, on_ends + 1)
  128. yield self.ELLIPSIS
  129. yield from range(number - on_each_side, number + 1)
  130. else:
  131. yield from range(1, number + 1)
  132. if number < (self.num_pages - on_each_side - on_ends) - 1:
  133. yield from range(number + 1, number + on_each_side + 1)
  134. yield self.ELLIPSIS
  135. yield from range(self.num_pages - on_ends + 1, self.num_pages + 1)
  136. else:
  137. yield from range(number + 1, self.num_pages + 1)

2.4 DjangoFilterBackend(后端过滤类):

  1. class DjangoFilterBackend(metaclass=RenameAttributes):
  2. filterset_base = filterset.FilterSet
  3. raise_exception = True
  4. @property
  5. def template(self):
  6. if compat.is_crispy():
  7. return 'django_filters/rest_framework/crispy_form.html'
  8. return 'django_filters/rest_framework/form.html'
  9. def get_filterset(self, request, queryset, view):
  10. '''
  11. 先调用self.get_filterset_class获取视图中的过滤集类filterset_class
  12. 如果filterset_class为none,则返回none,除此之外就返回filterset_class(**kwargs)
  13. '''
  14. filterset_class = self.get_filterset_class(view, queryset)
  15. if filterset_class is None:
  16. return None
  17. #调用self.get_filterset_kwargs返回一个字典有keys:queryset,request,data
  18. #其中的data就是url后的过滤参数request.query_params
  19. kwargs = self.get_filterset_kwargs(request, queryset, view)
  20. return filterset_class(**kwargs)
  21. def get_filterset_class(self, view, queryset=None):
  22. """
  23. 获取过滤集类
  24. """
  25. #从我们定义的视图中获取filterset_class属性
  26. filterset_class = getattr(view, 'filterset_class', None)
  27. #从我们定义的视图中获取filterset_fields属性
  28. filterset_fields = getattr(view, 'filterset_fields', None)
  29. #例如我们自定义的视图类提供了filter_classs属性并赋值了自定义的过滤类
  30. #如果filterset_class为none,并且定义的视图类中有filter_class属性
  31. if filterset_class is None and hasattr(view, 'filter_class'):
  32. utils.deprecate(
  33. "`%s.filter_class` attribute should be renamed `filterset_class`."
  34. % view.__class__.__name__)
  35. #从试图类中获取filter_classs属性并赋值给filterset_class
  36. filterset_class = getattr(view, 'filter_class', None)
  37. #如果filterset_fields为none,并且定义的视图类中有filter_fields属性
  38. if filterset_fields is None and hasattr(view, 'filter_fields'):
  39. utils.deprecate(
  40. "`%s.filter_fields` attribute should be renamed `filterset_fields`."
  41. % view.__class__.__name__)
  42. #从试图类中获取filter_fields属性并赋值给filterset_fields
  43. filterset_fields = getattr(view, 'filter_fields', None)
  44. #如果视图中的filterset_class属性不为空
  45. if filterset_class:
  46. filterset_model = filterset_class._meta.model
  47. # FilterSets不需要指定一个元类
  48. if filterset_model and queryset is not None:
  49. assert issubclass(queryset.model, filterset_model), \
  50. 'FilterSet model %s does not match queryset model %s' % \
  51. (filterset_model, queryset.model)
  52. return filterset_class
  53. #如果视图中的filterset_fields属性和queryset不为none
  54. if filterset_fields and queryset is not None:
  55. MetaBase = getattr(self.filterset_base, 'Meta', object)
  56. class AutoFilterSet(self.filterset_base):
  57. class Meta(MetaBase):
  58. model = queryset.model
  59. fields = filterset_fields
  60. return AutoFilterSet
  61. return None
  62. def get_filterset_kwargs(self, request, queryset, view):
  63. '''
  64. 返回一个字典有data,queryset,request
  65. request.query_params就是get请求url后拼接的过滤参数
  66. '''
  67. return {
  68. 'data': request.query_params,
  69. 'queryset': queryset,
  70. 'request': request,
  71. }
  72. def filter_queryset(self, request, queryset, view):
  73. filterset = self.get_filterset(request, queryset, view)
  74. if filterset is None:
  75. return queryset
  76. if not filterset.is_valid() and self.raise_exception:
  77. raise utils.translate_validation(filterset.errors)
  78. return filterset.qs
  79. def to_html(self, request, queryset, view):
  80. filterset = self.get_filterset(request, queryset, view)
  81. if filterset is None:
  82. return None
  83. template = loader.get_template(self.template)
  84. context = {'filter': filterset}
  85. return template.render(context, request)
  86. def get_coreschema_field(self, field):
  87. if isinstance(field, filters.NumberFilter):
  88. field_cls = compat.coreschema.Number
  89. else:
  90. field_cls = compat.coreschema.String
  91. return field_cls(
  92. description=str(field.extra.get('help_text', ''))
  93. )
  94. def get_schema_fields(self, view):
  95. # This is not compatible with widgets where the query param differs from the
  96. # filter's attribute name. Notably, this includes `MultiWidget`, where query
  97. # params will be of the format `<name>_0`, `<name>_1`, etc...
  98. assert compat.coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
  99. assert compat.coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
  100. try:
  101. queryset = view.get_queryset()
  102. except Exception:
  103. queryset = None
  104. warnings.warn(
  105. "{} is not compatible with schema generation".format(view.__class__)
  106. )
  107. filterset_class = self.get_filterset_class(view, queryset)
  108. return [] if not filterset_class else [
  109. compat.coreapi.Field(
  110. name=field_name,
  111. required=field.extra['required'],
  112. location='query',
  113. schema=self.get_coreschema_field(field)
  114. ) for field_name, field in filterset_class.base_filters.items()
  115. ]
  116. def get_schema_operation_parameters(self, view):
  117. try:
  118. queryset = view.get_queryset()
  119. except Exception:
  120. queryset = None
  121. warnings.warn(
  122. "{} is not compatible with schema generation".format(view.__class__)
  123. )
  124. filterset_class = self.get_filterset_class(view, queryset)
  125. if not filterset_class:
  126. return []
  127. parameters = []
  128. for field_name, field in filterset_class.base_filters.items():
  129. parameter = {
  130. 'name': field_name,
  131. 'required': field.extra['required'],
  132. 'in': 'query',
  133. 'description': field.label if field.label is not None else field_name,
  134. 'schema': {
  135. 'type': 'string',
  136. },
  137. }
  138. if field.extra and 'choices' in field.extra:
  139. parameter['schema']['enum'] = [c[0] for c in field.extra['choices']]
  140. parameters.append(parameter)
  141. return parameters