首页 > 编程语言 >DRF之排序类源码分析

DRF之排序类源码分析

时间:2024-04-23 19:25:17浏览次数:28  
标签:get ordering fields self view 源码 排序 DRF

DRF之排序类源码分析

【一】排序类介绍

  • 在Django REST framework (DRF)中,排序类用于处理API端点的排序操作,允许客户端请求按特定字段对数据进行升序或降序排序。
  • 排序类是一种特殊的过滤类
  • DRF提供了内置的排序类,并且你也可以自定义排序类以满足特定的需求。

【二】内置排序类OrderingFilter

  • rest_framework.filters.OrderingFilter:这是DRF默认的排序类。
  • 它允许客户端在API请求中使用 ?ordering= 参数来指定要排序的字段。
  • 例如,?ordering=-created_at 将按 created_at 字段降序排序。

【1】使用

from rest_framework.filters import OrderingFilter

class MyModelListView(ListAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer
    filter_backends = [OrderingFilter]  # 添加你的自定义排序类
    ordering_fields = ['field1', 'field2']  # 定义允许排序的字段
  • 执行流程
    • 当一个API请求到达时,Django REST framework将会执行视图的 get_queryset 方法来获取查询集。
    • 如果使用了 OrderingFilter 排序类,它会检查请求中是否包含 ?ordering= 参数。
    • 如果请求中包含 ?ordering= 参数,OrderingFilter 会根据参数的值对查询集进行排序。
    • 排序后的查询集将传递给视图进行进一步处理和返回。

【2】源码分析

class OrderingFilter(BaseFilterBackend):
    # The URL query parameter used for the ordering.
    ordering_param = api_settings.ORDERING_PARAM
    ordering_fields = None
    ordering_title = _('Ordering')
    ordering_description = _('Which field to use when ordering the results.')
    template = 'rest_framework/filters/ordering.html'
	
    # 获取客户端请求中的排序参数并返回排序顺序
    def get_ordering(self, request, queryset, view):
        """
        Ordering is set by a comma delimited ?ordering=... query parameter.

        The `ordering` query parameter can be overridden by setting
        the `ordering_param` value on the OrderingFilter or by
        specifying an `ORDERING_PARAM` value in the API settings.
        """
        # 首先从请求参数中获取排序参数(例如,?ordering=)
        params = request.query_params.get(self.ordering_param)
        if params:
            # 然后将其拆分成字段名
            fields = [param.strip() for param in params.split(',')]
            # 然后使用 remove_invalid_fields 方法来移除无效的字段名
            ordering = self.remove_invalid_fields(queryset, fields, view, request)
            # 如果排序参数有效
            if ordering:
                # 将返回排序顺序
                return ordering

        # No ordering was included, or all the ordering fields were invalid
        # 如果没有提供排序参数或所有字段都无效,将调用 get_default_ordering 方法返回默认排序
        return self.get_default_ordering(view)
	
    # 获取视图的默认排序顺序
    def get_default_ordering(self, view):
        # 如果视图类中定义了 ordering 属性
        ordering = getattr(view, 'ordering', None)
        if isinstance(ordering, str):
            # 它将返回该属性的值
            return (ordering,)
        # 否则将返回 None
        return ordering
	
    # 获取默认允许排序的字段
    def get_default_valid_fields(self, queryset, view, context={}):
        # If `ordering_fields` is not specified, then we determine a default
        # based on the serializer class, if one exists on the view.
        
        # 如果视图有 get_serializer_class 方法
        if hasattr(view, 'get_serializer_class'):
            try:
                # 尝试从序列化器类中获取字段列表
                serializer_class = view.get_serializer_class()
            except AssertionError:
                # Raised by the default implementation if
                # no serializer_class was found
                serializer_class = None
        else:
            serializer_class = getattr(view, 'serializer_class', None)
		
        # 如果没有序列化器类,将引发 ImproperlyConfigured 异常
        if serializer_class is None:
            msg = (
                "Cannot use %s on a view which does not have either a "
                "'serializer_class', an overriding 'get_serializer_class' "
                "or 'ordering_fields' attribute."
            )
            raise ImproperlyConfigured(msg % self.__class__.__name__)
		
        # 获取到模型类的模型
        model_class = queryset.model
        # 校验模型类中的字段
        model_property_names = [
            # 'pk' is a property added in Django's Model class, however it is valid for ordering.
            attr for attr in dir(model_class) if isinstance(getattr(model_class, attr), property) and attr != 'pk'
        ]	
        
		# 列出模型字段和查询注释字段
        return [
            (field.source.replace('.', '__') or field_name, field.label)
            for field_name, field in serializer_class(context=context).fields.items()
            if (
                not getattr(field, 'write_only', False) and
                not field.source == '*' and
                field.source not in model_property_names
            )
        ]
	
    # 获取允许排序的字段
    def get_valid_fields(self, queryset, view, context={}):
        # 如果视图定义了 ordering_fields,将返回该字段,否则将根据 ordering_fields 的值执行不同的逻辑
        valid_fields = getattr(view, 'ordering_fields', self.ordering_fields)
		
        # 如果 ordering_fields 是 None
        if valid_fields is None:
            # Default to allowing filtering on serializer fields
            # 将调用 get_default_valid_fields 方法获取默认字段列表
            return self.get_default_valid_fields(queryset, view, context)
		
        # 如果 ordering_fields 是 __all__
        elif valid_fields == '__all__':
            # View explicitly allows filtering on any model field
            
            # 将允许对模型的所有字段进行排序
            valid_fields = [
                (field.name, field.verbose_name) for field in queryset.model._meta.fields
            ]
            valid_fields += [
                (key, key.title().split('__'))
                for key in queryset.query.annotations
            ]
        else:
            # 否则,将返回视图中定义的排序字段
            valid_fields = [
                (item, item) if isinstance(item, str) else item
                for item in valid_fields
            ]
		
        # 返回允许排序的字段
        return valid_fields
	
    # 移除无效的排序字段
    def remove_invalid_fields(self, queryset, fields, view, request):
        
        # 接受一个字段列表,然后使用 get_valid_fields 方法获取允许的字段列表
        valid_fields = [item[0] for item in self.get_valid_fields(queryset, view, {'request': request})]
		
        # 校验有效字段的列表
        def term_valid(term):
            if term.startswith("-"):
                term = term[1:]
            return term in valid_fields
		
        # 最后,它将返回一个仅包含有效字段的列表
        return [term for term in fields if term_valid(term)]
	
    # 对查询集进行排序
    def filter_queryset(self, request, queryset, view):
        # 调用 get_ordering 方法获取排序顺序
        ordering = self.get_ordering(request, queryset, view)

        if ordering:
            # 然后使用 order_by 方法对查询集进行排序
            return queryset.order_by(*ordering)
		
        # 如果没有提供排序参数,将返回原始查询集
        return queryset
	
    # 为HTML模板提供上下文数据
    def get_template_context(self, request, queryset, view):
        # 首先,它调用 get_ordering 方法获取当前的排序顺序(如果有的话),并将其存储在变量 current 中
        current = self.get_ordering(request, queryset, view)
        #  # 如果没有排序顺序,将设置 current 为 None
        current = None if not current else current[0]
        
        # 创建一个空列表 options,用于存储可用的排序选项
        options = []
        
        # 创建一个上下文字典 context 包含以下键值对
        context = {
            # 存储请求对象,以便在模板中访问请求信息
            'request': request,
            # 存储当前排序状态(可能为 None)
            'current': current,
            # 存储排序参数名称(例如,ordering)
            'param': self.ordering_param,
        }
        
        # 循环遍历可用的排序字段
        # 使用 get_valid_fields 方法获取可用的排序字段
        for key, label in self.get_valid_fields(queryset, view, context):
            
            # 将字段名称和升序排序标签添加到 options 列表。
            options.append((key, '%s - %s' % (label, _('ascending'))))
            
            # 将字段名称加上 '-' 前缀和降序排序标签添加到 options 列表。
            options.append(('-' + key, '%s - %s' % (label, _('descending'))))
            
        # 将包含排序选项的列表添加到上下文字典中,以便在模板中访问
        context['options'] = options
        
        # 返回一个包含有关当前排序状态和可用排序选项的上下文字典
        return context
	
    # 生成HTML表示的排序控件
    def to_html(self, request, queryset, view):
        
        # 获取渲染器
        template = loader.get_template(self.template)
        
        # 获取上下文对象
        context = self.get_template_context(request, queryset, view)
        
        # 使用模板来渲染排序控件,并返回HTML代码
        return template.render(context)

    def get_schema_fields(self, view):
        # 这是一个断言语句,用于确保Core API库已安装。
        # 如果未安装,将引发AssertionError异常,提示用户需要安装Core API库才能使用这个方法
        assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`'
        # 用于确保Core Schema库已安装。
        # 如果未安装,将引发AssertionError异常,提示用户需要安装Core Schema库才能使用这个方法。
        assert coreschema is not None, 'coreschema must be installed to use `get_schema_fields()`'
        
        # 如果Core API和Core Schema库都已安装,该方法将返回一个包含排序参数描述信息的列表。
        return [
            # 具体来说,它创建了一个coreapi.Field对象,该对象描述了排序参数的属性
            coreapi.Field(
                # 定义参数的名称,通常是ordering
                name=self.ordering_param,
                # 指示参数是否是必需的。在这里,它设置为False,因为排序参数是可选的
                required=False,
                # 指示参数在请求的查询字符串中
                location='query',
                # 定义参数的架构。在这里,它指定参数的类型为字符串,并提供了标题和描述信息,这些信息将出现在API文档中
                schema=coreschema.String(
                    title=force_str(self.ordering_title),
                    description=force_str(self.ordering_description)
                )
            )
        ]
	
    # 返回一个包含描述排序参数的操作参数信息的列表
    def get_schema_operation_parameters(self, view):
        return [
            {
                #  定义参数的名称,通常是 ordering
                'name': self.ordering_param,
                # 指示参数是否是必需的。在这里,它设置为 False,因为排序参数是可选的。
                'required': False,
                # 指示参数在请求中的位置。在这里,它设置为 'query',表示排序参数位于查询字符串中。
                'in': 'query',
                # 提供有关参数的描述信息,使用 force_str(self.ordering_description) 获取排序参数的描述
                'description': force_str(self.ordering_description),
                # 定义参数的架构,包含有关参数类型的信息。在这里,它指定参数的类型为字符串,表示排序参数的值应该是字符串类型
                'schema': {
                    'type': 'string',
                },
            },
        ]

【三】自定义排序类

【1】使用

from rest_framework.filters import OrderingFilter

class CustomOrderingFilter(OrderingFilter):
    def get_ordering(self, request, queryset, view):
        # 获取客户端传递的排序参数
        ordering = request.query_params.get('ordering')
        if ordering:
            # 在此处可以根据自定义逻辑修改排序方式
            return [ordering]
        return super().get_ordering(request, queryset, view)
class MyModelListView(ListAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer
    filter_backends = [CustomOrderingFilter]  # 添加你的自定义排序类
    ordering_fields = ['field1', 'field2']  # 定义允许排序的字段

【2】分析

  • 继承 OrderingFilter
  • 重写 get_ordering 方法
  • 自定义 过滤条件
  • 将过滤后的视图视图集返回给视图函数进一步调用

标签:get,ordering,fields,self,view,源码,排序,DRF
From: https://www.cnblogs.com/ssrheart/p/18153599

相关文章

  • DRF之请求执行流程和APIView源码分析
    DRF之请求执行流程和APIView源码分析【一】路由入口fromdjango.contribimportadminfromdjango.urlsimportpathfrombookimportviewsurlpatterns=[path('admin/',admin.site.urls),#原来的路由写法#path('test_http/',views.TestHttpResponse),......
  • DRF之Response源码分析
    DRF之Response源码分析【一】响应类的对象Response源码【1】路由fromdjango.contribimportadminfromdjango.urlsimportpathfrombookimportviewsurlpatterns=[path('admin/',admin.site.urls),path('test/',views.TestView.as_view()),]【2】视......
  • DRF之频率组件源码分析
    DRF之频率组件源码分析【一】频率组件介绍DjangoRestFramework(DRF)中的频率组件是用于限制API端点的访问频率的一种机制。频率组件可以帮助你控制用户对API的请求频率,以防止滥用和DDoS攻击。比如某个接口,一分钟只能访问5次,超过了就得等按IP地址限制按用户id限制【......
  • DRF之权限组件源码分析
    DRF之权限组件源码分析【一】权限组件介绍DjangoRESTframework(DRF)中的权限组件用于控制API的访问权限。DRF内置了多个常用的权限类,同时也允许你创建自定义的权限类以满足特定需求。【二】内置权限类IsAuthenticated:要求用户在访问API时进行身份验证,即用户必须登录。IsA......
  • DRF之Request源码分析
    DRF之Request源码分析【一】路由入口fromdjango.contribimportadminfromdjango.urlsimportpathfrombookimportviewsurlpatterns=[path('admin/',admin.site.urls),path('test/',views.TestView.as_view()),path('test_http/&#......
  • 排序4-希尔排序
    排序4-希尔排序插入排序在以下情况时效率较高当元素序列基本有序元素个数较少希尔排序是对插入排序的优化希尔排序先将元素分组(通常为总长度的一半,例如有8个数据量,则将数据分为4组,每组2个数据),然后再对每一组元素单独进行插入排序,创造出满足上述2个条件......
  • 归并排序
    归并排序是一种基于分治的算法,下面给出我的数组式(半数组,有偏移理解)代码:点击查看代码//注意:我的答案数组下标开始为1,且所有操作区间均为闭区间//时间复杂度:稳定o(nlogn)//空间复杂度:o(n),栈空间:o(nlogn),若开全局数组则可忽略栈空间#include<bits/stdc++.h>using......
  • 快速排序法
    第一种写法:定标杆在起点时间复杂度:平均o(nlogn),最坏o(n^2)代码如下:点击查看代码#include<bits/stdc++.h>usingnamespacestd;voidquick_sort(inta[],intb,inte){if(b>=e)return;inttemp=a[b];inti=b,j=e;while(i<j){......
  • 渲染 | Gaussian Splatting 源码解析
    TODO:GS组成pointcloudrenderflowoptimize仓库结构仓库由C++/CUDA和Python组成,CUDA包括diff-rasterization,Python包括optimize。CUDA部分依赖glm,只用了glm的vector数据类型。Rendering(C++/CUDAPart)Python-CUDA接口CUDA向Python暴露的就......
  • 说说你对选择排序的理解?如何实现?应用场景?
    一、是什么选择排序(Selectionsort)是一种简单直观的排序算法,无论什么数据进去都是 O(n²)的时间复杂度,所以用到它的时候,数据规模越小越好其基本思想是:首先在未排序的数列中找到最小(or最大)元素,然后将其存放到数列的起始位置然后再从剩余未排序的元素中继续寻找最小(or最大)......