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 方法
  • 自定义 过滤条件
  • 将过滤后的视图视图集返回给视图函数进一步调用

DRF之排序类源码分析的更多相关文章

  1. List 接口以及实现类和相关类源码分析

    List 接口以及实现类和相关类源码分析 List接口分析 接口描述 用户可以对列表进行随机的读取(get),插入(add),删除(remove),修改(set),也可批量增加(addAll),删除( ...

  2. DRF的Serializer组件(源码分析)

    DRF的Serializer组件(源码分析) 1. 数据校验 drf中为我们提供了Serializer,他主要有两大功能: 对请求数据校验(底层调用Django的Form和ModelForm) 对数据 ...

  3. DRF框架(一)——restful接口规范、基于规范下使用原生django接口查询和增加、原生Django CBV请求生命周期源码分析、drf请求生命周期源码分析、请求模块request、渲染模块render

    DRF框架    全称:django-rest framework 知识点 1.接口:什么是接口.restful接口规范 2.CBV生命周期源码 - 基于restful规范下的CBV接口 3.请求组件 ...

  4. drf 简介以及部分源码分析

    目录 复习 drf框架 全称:django-rest framework 知识点 接口 restful接口规范 基于restful规范的原生Django接口 主路由:url.py api组件的子路由: ...

  5. 探索drf执行流程之APIView源码分析

    Django REST framework 简介 现在新一代web应用都开始采用前后端分离的方式来进行,淘汰了以前的服务器端渲染的方式.而实现前后端分离是通过Django REST framework ...

  6. drf 视图使用及源码分析

    前言 drf视图的源码非常的绕,但是实现的功能却非常的神奇. 它能够帮你快速的解决ORM增删改查的重复代码,非常的方便好用. 下面是它源码中的一句话: class ViewSetMixin: &quo ...

  7. drf 认证校验及源码分析

    认证校验 认证校验是十分重要的,如用户如果不登陆就不能访问某些接口. 再比如用户不登陆就不能够对一个接口做哪些操作. drf中认证的写法流程如下: 1.写一个类,继承BaseAuthenticatio ...

  8. DRF认证流程及源码分析

    认证 前言 用户验证用户是否合法登陆. 部分内容在DRF视图的使用及源码流程分析讲解,建议先看讲解视图的这篇文章. 使用流程 认证使用的方法流程如下: 自定义认证类,继承BaseAuthenticat ...

  9. drf快速使用 CBV源码分析 drf之APIView分析 drf之Request对象分析

    目录 序列化和反序列化 drf介绍和安装 使用原生django写接口 django DRF安装 drf快速使用 模型 序列化类 视图 路由 datagrip 使用postman测试接口 CBV源码分析 ...

  10. ApiView/Request类源码分析/序列化器

    内容概要 ApiView+JsonResponse编写接口 ApiView+Response编写接口 ApiView源码解析 Request对象源码分析 序列化器介绍和快速使用/反序列化 反序列化的校 ...

随机推荐

  1. Delphi 中拖动无边框窗口的5种方法

    1.MouseMove事件中加入: // ReleaseCapture; // Perform(WM_SYSCOMMAND, $F017 , 0); 2.MouseDown事件中加入: // POST ...

  2. SSH登录方式及如何防止SSH端口被扫

    ssh登录服务器的方式有三种:密码登录,公钥登录,证书登录.同时,密码登录有被破解的风险,网络上也有很多在扫描ssh端口的主机. 比如: 这里175.178.62.36是一个来自广东的服务器,17次尝 ...

  3. 文字像素(.NET)

    无图言* 代码实现 新建一个控制台应用程序, 调整 Program.cs 文件内容如下: using System; using System.Drawing; namespace ConsoleAp ...

  4. FastAPI 核心安全功能与模板渲染的完整示:登录、CSRF、JWT、会话、认证和缓存功能

    以下是一个整合 FastAPI 核心安全功能与模板渲染的完整示例,基于多个技术文档的最佳实践,包含登录.CSRF.JWT.会话.认证和缓存功能: from datetime import dateti ...

  5. LeetCode1464. 数组中两元素的最大乘积-JAVA

    题目 给你一个整数数组 nums,请你选择数组的两个不同下标 i 和 j,使 (nums[i]-1)*(nums[j]-1) 取得最大值.请你计算并返回该式的最大值. 示例 1: 输入:nums = ...

  6. 1.net core 工作流WorkFlow流程(介绍)

    WikeFlow官网:www.wikesoft.com WikeFlow学习版演示地址:workflow.wikesoft.com WikeFlow学习版源代码下载:https://gitee.com ...

  7. python,去掉“xa0”和“\r\n”

    爬小说网站,输出内容有时候会出现下图字符 首先,去掉"xa0" s = 'T-shirt\xa0\xa0短袖圆领衫,体恤衫\xa0' out = "".join ...

  8. 细节解析 JavaScript 中 bind 函数的模拟实现

    大家的阅读是我发帖的动力,本文首发于我的博客:deerblog.gu-nami.com/,欢迎大家来玩,转载请注明出处喵. 前言 bind是一个改变函数this指针指向的一个常用函数,经常用在涉及th ...

  9. SQL 日常练习 (十九)

    趁热打铁, 一波 SQL 继续带走 ~~ 虽然是假期, 但我也不想出去逛, 宅着也不想看书和思考人生, 除了做饭, 就更多对着电脑发呆. 时而看了下微信群, 初中小伙伴结合, 祝福寄语 和 随份子 都 ...

  10. Mac M1 安装python3.6.x

    在mac M1上通过pyvenv 直接安装python3.6.x 会失败. 后来发现其实python官方直接提供了m1的pkg包,就不需要再重新编译安装了. 进入python官方为macos提供的各版 ...