【一】DRF入门规范
-
前后端开发模式:
- 混合:
- 前后端代码交织在一起,同一份代码中既包含前端逻辑又包含后端逻辑。
- 这种模式通常在小型项目或者简单的页面中使用,便于快速开发和维护。
- 分离:
- 前后端代码分离开发,前端专注于用户界面设计和交互逻辑,后端则负责数据处理和业务逻辑。
- 这种模式通常在大型项目或者需要高度可扩展性、可维护性的项目中使用,可以更好地实现前后端分工合作。
- 混合:
-
API接口
- 地址:
- API接口的URL地址,用于标识特定资源或执行特定操作。
- 例如:http://example.com/api/users
- 请求方法:
- HTTP协议定义了多种请求方法,常见的有GET、POST、PUT、DELETE等。
- 请求方法用于指定对资源的操作类型。
- 例如:GET表示获取资源,POST表示创建资源,PUT表示更新资源,DELETE表示删除资源。
- 请求参数:
- 请求参数用于传递给后端的数据
- 可以是查询参数(在URL中拼接)、请求头参数或请求体参数。
- 请求参数可以包括筛选条件、分页信息、身份验证信息等。
- 例如:
GET /api/users?name=John&age=25
- 返回值:
- API接口响应的数据,通常以JSON格式返回。
- 返回值可以包含请求的结果、错误信息以及其他元数据。
- 例如:
- 地址:
-
postman的使用
- Postman是一款常用的API接口测试工具,可以用于发送HTTP请求、查看响应结果、调试接口等。
- 使用Postman可以轻松创建请求、添加请求参数、设置请求头,还可进行断言和验证。
- 此外,Postman还提供了协作和文档化的功能,方便团队合作和接口文档编写。
-
序列化与反序列化
- 序列化:
- 将对象的状态转换为可以存储或传输的形式的过程称为序列化。
- 在Web开发中,常见的序列化格式包括JSON、XML等。
- 通过序列化,对象的属性值可以被转换为字符串或字节流进行传输。
- 反序列化:
- 将已序列化的数据恢复为对象的过程称为反序列化。
- 反序列化过程将字符串或字节流转换为对象,使得可以读取对象的属性值和调用对象的方法。
- 序列化:
-
restful规范
- 使用HTTP协议定义请求方法:GET、POST、PUT、DELETE等。
- 使用URL地址标识资源:每个资源都有一个唯一的URL地址。
- 使用合适的HTTP状态码:响应请求时,使用正确的HTTP状态码表示请求的结果。
- 使用合适的HTTP动词:HTTP动词用于表示对资源的操作类型。
- 使用标准的数据格式:通常使用JSON格式传输数据。
-
在Django中写符合规范的接口
- Django Rest Framework(DRF)是一个用于构建Web API的强大工具包。
- 通过DRF,可以方便地编写符合RESTful规范的接口。
- 在Django中使用DRF,可以通过定义序列化器(Serializer)来指定API接口的输入输出格式,并通过视图集(ViewSet)或通用视图类(GenericAPIView)定义API接口的逻辑。
- DRF还提供了丰富的验证、权限控制、分页和过滤等功能,可以帮助开发者快速构建高质量的API接口。
- Django Rest Framework(DRF)是一个用于构建Web API的强大工具包。
【二】CBV源码分析
- 路由中
- 路由中的视图类.as_view():
- 在路由中,我们通常将视图类作为视图处理函数来处理请求。
- 但是,路由系统需要将类视图转换成可调用的函数。
- 因此,我们使用
.as_view()
方法将视图类转换为可调用的函数。 - 这样,当请求匹配到该路由时,将调用该视图类的
.as_view()
方法。
- Django的View的as_view():
- Django的
View
类是CBV的基类。 as_view()
是View
类中的一个类方法,用于生成可调用的视图函数。- 它实际上返回一个闭包函数,该函数在调用时会创建视图类的实例,并调用其
dispatch()
方法进行请求处理。
- Django的
- 执行结果的 view 内存地址:
- 调用视图类的
.as_view()
方法后,返回的闭包函数表示视图函数。 - 执行该闭包函数时,会创建视图类的实例,并调用其
dispatch()
方法处理请求。 - 所以得到的执行结果是视图类的实例在内存中的地址。
- 调用视图类的
- 请求过来,路由匹配成功:
- 当HTTP请求到达Django应用程序时,URL路由系统会根据请求的路径查找匹配的路由。
- 如果路由匹配成功,就会找到与之对应的视图处理函数或视图类。
- view(request):
- 当路由匹配成功后,会将HTTP请求对象作为参数传递给视图处理函数或视图类。
- 在CBV中,这个参数通常被命名为
request
。 - 所以
view(request)
的意思就是将获取到的HTTP请求对象传递给视图类。
- return self.dispatch():
- 在视图类的代码中,通常会调用
self.dispatch()
方法。 - 该方法实际上是
View
类中的一个方法,用于根据不同的HTTP请求方法来分发请求到对应的方法处理函数。 - 例如,对于GET请求,会调用视图类中的
get()
方法进行处理。
- 在视图类的代码中,通常会调用
- View的dispatch:
dispatch()
方法负责根据不同的HTTP请求方法调用对应的方法处理函数。- 它会通过Python的反射机制,根据请求的方法动态获取并执行对应的方法。
- 通过反射,不同的请求会执行视图类中的不同方法:
- 反射是Python的一种特性,通过字符串的形式来调用对象的属性或方法。
- 在CBV中,当请求到达时,
dispatch()
方法会通过反射来调用视图类中对应请求方法的处理函数 - 例如
get()
、post()
等。
- 路由中的视图类.as_view():
- 综上所述,当请求到达时,Django的路由系统会匹配对应的路由,并通过
.as_view()
方法将视图类转换为可调用的函数。- 然后,传递请求对象给该函数并执行,最终调用视图类的
dispatch()
方法来根据不同的请求方法调用对应的处理函数。- 通过反射机制,不同的请求会执行视图类中不同的方法。
【三】APIView的执行流程
-
APIView继承了View:
APIView
是Django REST Framework(DRF)提供的一个类,用于处理API请求。- 它继承了Django的
View
类,既包含了Django的视图功能,又增加了一些处理API请求的特性。
-
视图类的class BookView(APIView):
- 在使用DRF构建API时,我们可以创建一个继承自
APIView
的自定义视图类。 - 这里的
BookView
就是一个自定义的视图类,它继承自APIView
,用于处理关于图书的API请求。
- 在使用DRF构建API时,我们可以创建一个继承自
-
路由中视图类.as_view():
- 类视图无法直接用于路由系统,因此需要将视图类转换为可调用的函数。
- 在路由中,我们通常会调用视图类的
.as_view()
方法来将其转换为可调用的函数。
-
drf的APIView的as_view():
- 在DRF中,
APIView
的.as_view()
方法是将视图类转换为可调用函数的入口。 - 在调用
APIView
的.as_view()
方法时,它会调用父类View
的.as_view()
方法。
- 在DRF中,
-
调用父类的as_view去除csrf:
- 父类
View
的.as_view()
方法负责创建一个闭包函数,并在创建前通过装饰器去除了Django的CSRF保护机制。 - 这是因为在API开发中,很少使用Django的CSRF保护。
- 父类
-
View的as_view内部的view闭包函数 (相当于加了个装饰器):
- 在
View
的.as_view()
方法内部,会创建一个闭包函数(wrapper function)。 - 这个闭包函数实际上包装了视图类的实例化和请求处理过程。
- 通过闭包函数的形式,可以将视图类中的方法转换为可调用的函数,并将请求传递给这些方法进行处理。
- 在
-
return self.dispatch():
- 在视图类的闭包函数中,会调用
self.dispatch()
方法。 - 该方法实际上是
APIView
类中的一个方法,用于处理请求的分发过程。
- 在视图类的闭包函数中,会调用
-
APIView的dispatch():
dispatch()
方法是APIView
类中定义的主要方法,负责根据不同的HTTP请求方法调用对应的方法处理函数。- 它通过检查请求方法(GET、POST、PUT、DELETE等)来决定调用视图类中的哪个方法来处理请求。
- 综上所述,当使用DRF编写API时,我们可以创建一个继承自
APIView
的自定义视图类。- 在路由中,调用视图类的
.as_view()
方法将其转换为可调用的函数。APIView
的.as_view()
方法会调用父类View
的.as_view()
方法生成一个闭包函数,并去除了Django的CSRF保护。- 通过调用闭包函数,会实例化视图类,并调用
dispatch()
方法进行请求分发。dispatch()
方法根据不同的HTTP请求方法,调用视图类中对应的方法处理函数。
【四】Request类的源码分析
-
request.data
:request.data
是一个属性,用于获取请求的数据。- 它是一个字典对象,包含了请求中的表单数据、JSON数据或其他非文件类型数据。
-
request.query_params
request.query_params
也是一个属性,用于获取请求的查询参数。- 它是一个字典对象,包含了从URL中提取的查询参数键值对。
-
request.FILES
request.FILES
是一个属性,用于获取请求中的文件数据。- 它是一个类似字典的对象,包含了上传的文件数据。
-
request.method
request.method
是一个属性,用于获取HTTP请求的方法。- 例如,GET、POST、PUT、DELETE等。
-
request.path
request.path
是一个属性,用于获取请求的路径部分。- 它是一个字符串,表示了请求的URL中的路径部分。
-
...
-
重写了魔法方法
- 除了上述属性之外,Request类还重写了魔法方法
__getattr__
。- 这个魔法方法在对象.属性访问时会自动触发,当访问的属性不存在时,会执行
__getattr__
方法中的逻辑。
- 这个魔法方法在对象.属性访问时会自动触发,当访问的属性不存在时,会执行
- 在Request类中,
__getattr__
方法通过反射技术实现了对self._request
属性的访问。self._request
属性是一个低级的HttpRequest实例,它包含了更多原生的请求数据。
- 通过重写了
__getattr__
方法,Request类可以在访问不存在的属性时,将请求委托给self._request
对象,并从中获取对应的属性值。
- 除了上述属性之外,Request类还重写了魔法方法
【五】序列化组件
-
序列化组件主要提供了两个功能:
- 反序列化和序列化校验。
- 反序列化指的是将传入的数据转换为Python对象的过程,通常用于处理用户提交的数据。
- 序列化校验则是对反序列化后的数据进行验证,确保数据的完整性和准确性。
- 反序列化和序列化校验。
-
Serialzier
Serializer
是序列化组件的基类,用于定义序列化类的结构和行为。- 在示例中,定义了一个名为
BookSerializer
的序列化类,通过继承Serializer
类来创建自定义的序列化器。
-
定义序列化类
class BookSerializer(Serializer): name = serializers.CharField()
- 在示例中,
BookSerializer
是一个简单的序列化类,仅包含一个名为name
的字段。 - 字段是序列化类的基本组成部分,用于定义要序列化和反序列化的数据字段。
- 在示例中,
-
使用序列化类
# 多个数据 ser = BookSerializer(instance=queryset,many=True) # 单个数据 ser = BookSerializer(instance=queryset) # 序列化后的数据 ser.data # 字典
- 通过实例化序列化类并传入
instance
参数来使用序列化类进行序列化。 - 当需要序列化多个对象时,可以将
many
参数设置为True
,并传入一个查询集。序列化后的数据可以通过访问ser.data
属性来获取,该属性返回一个字典对象。
- 通过实例化序列化类并传入
-
序列化字段
- 序列化字段定义了如何处理特定类型的数据。
- 在示例中未详细说明具体的序列化字段类型
- 但常用的一些字段类型包括:
CharField
、IntegerField
、BooleanField
等。
- 但常用的一些字段类型包括:
- 此外,还有一些复杂的字段类型,如
ListField
和DictField
- 用于处理列表和字典类型的数据。
-
字段参数
- 字段参数用于对序列化字段进行配置和校验。
- 其中
read_only
参数指定字段只用于序列化而不可用于反序列化write_only
参数指定字段只用于反序列化而不可用于序列化。
-
反序列化校验
- 序列化组件提供了多种校验方式,包括字段自身的校验、局部钩子函数以及全局钩子函数。
- 局部钩子函数使用
validate_字段名
的形式定义,用于对特定字段进行校验。 - 全局钩子函数命名为
validate
,用于对整个数据进行校验。
-
反序列化保存
- 进行完反序列化校验后,可以通过调用
ser.save()
方法将数据保存到数据库- 判断instance是否存在,如果不存在,就是调用ser的create方法
- 在序列化类中重写 create 方法
- 进行完反序列化校验后,可以通过调用
-
反序列化更新
- 进行完反序列化校验后,可以通过调用
ser.save()
方法将数据保存到数据库- 判断instance是否存在,如果存在,就是调用ser的update方法
- 在序列化类中重写 update方法
- 进行完反序列化校验后,可以通过调用
-
ModelSerializer的使用
ModelSerializer
是Serializer
的子类,用于简化与模型的序列化和反序列化。- 与普通序列化器相比,
ModelSerializer
不需要写明每个字段 - 它将自动映射到关联的模型字段
- 并提供了一些默认行为。
- 与普通序列化器相比,
- extra_kwargs携带其他参数
- 其他跟Serializer一样
-
序列化,定制返回格式
- 通过
source
参数可以定制序列化字段的来源- 在源数据中使用不同的名称或路径来匹配特定字段。
-
SerializerMethodField
- 可以在序列化类中定义一个名为
get_字段名
的方法 - 根据业务逻辑生成特定的返回值。
- 可以在序列化类中定义一个名为
- 在表模型中写:
- 写方法,返回字典,列表,字符串
- 在序列化类中可以使用ListField,DictField
- 通过
- 总结来说,序列化组件在Web开发中扮演着重要的角色,能够帮助开发者处理数据的序列化和反序列化,并提供了灵活的配置和校验机制。
- 开发者可以根据实际需求定义序列化类,使用不同的字段和参数,以及利用钩子函数进行数据校验和处理。
- 同时,
ModelSerializer
进一步简化了与模型之间的序列化操作。
【六】请求与响应
- Request类的请求
- 接受前端传入的编码格式:json,urlencoded,form-data
- 局部配置
- 全局配置
- Request的源码
- 接受前端传入的编码格式:json,urlencoded,form-data
- Response类的响应
- 前端看到的形式(浏览器,json)
- 源码分析
- data
- headers
- status_code
- Request 类用于处理客户端发起的请求。
- 编码格式(json、urlencoded、form-data),Request 类能够接收和解析前端传入的数据。
- 这些数据可以通过局部配置或全局配置进行处理。
- 局部配置:指的是在每个请求中针对特定的数据源进行配置,如指定请求头信息、编码格式等。
- 全局配置:指的是在整个应用中设置默认的请求配置,以便在每个请求中自动使用这些配置。
- Request 类的源码负责解析请求中的各个部分
- 包括 URL、方法、请求体以及用户代理等等
- 并提供了相应的属性和方法供开发者使用。
- Response 类用于构建服务器端响应并发送给客户端。
- 响应的形式可以是浏览器可直接显示的内容,也可以是序列化为 JSON 格式的数据。
- 前端看到的形式:
- 通常情况下,前端会在浏览器中看到 HTML 内容。
- 此外,还可以返回 JSON 格式的数据,前端可以通过 JavaScript 进行解析和处理。
- Response 类的源码包含几个关键部分
- data:
- 响应内容的主体。这通常是通过模板引擎或其他方式生成的 HTML 内容。
- headers:
- 响应头部的配置。
- 可以设置 Content-Type、Cache-Control 等信息。
- status_code:
- 响应状态码,用于表示请求的处理结果。
- 常见的状态码有 200(成功)、404(找不到资源)等。
- Response 类提供了一系列方法来构建和发送响应,最终将响应发送给客户端。
- 通过设置正确的状态码、响应头和返回内容,开发者可以自定义响应的形式和内容。
【七】视图组件
【1】两个视图基类
APIView
APIView
是 Django REST Framework 提供的一个基类,用于创建基于函数或基于类的视图。- 使用
APIView
可以根据需求自定义请求处理逻辑,对于简单的接口逻辑,可以直接继承APIView
类。
GenericAPIView
GenericAPIView
是APIView
的一个子类,提供了一些常用的通用方法和属性,使开发者能够更方便地编写视图。- 通常与
Mixin
类一起使用,通过继承GenericAPIView
和混入相应的功能类可以快速建立功能完善的视图。
【2】5 个视图扩展类
视图扩展类用于与
GenericAPIView
或其子类配合使用,提供常用的 CRUD(创建、读取、更新和删除)操作。
ListAPIView
- 继承自
GenericAPIView
和ListMixin
,实现获取多条记录的功能。
CreateAPIView
- 继承自
GenericAPIView
和CreateModelMixin
,实现创建记录的功能。
DestroyAPIView
- 继承自
GenericAPIView
和DestroyModelMixin
,实现删除记录的功能。
UpdateAPIView
- 继承自
GenericAPIView
和UpdateModelMixin
,实现更新记录的功能。
RetrieveAPIView
- 继承自
GenericAPIView
和RetrieveModelMixin
,实现获取单条记录的功能。
【3】9个视图子类
视图子类用于与
GenericAPIView
或其子类配合使用,提供特定的功能和集成多个操作。
CreateModelMixin
- 该混入类提供
create()
方法 - 用于根据传入的 POST 请求数据创建模型的新对象实例
ListModelMixin
- 该混入类提供
list()
方法 - 用于检索模型的对象列表,并将其序列化为响应数据
RetrieveModelMixin
- 该混入类提供
retrieve()
方法 - 根据提供的标识符或查找字段检索模型的单个对象实例
DestroyModelMixin
- 该混入类提供
destroy()
方法 - 根据提供的标识符或查找字段处理删除模型的单个对象实例
UpdateModelMixin
- 该混入类提供
update()
方法 - 根据提供的标识符或查找字段处理更新模型的单个对象实例
- 上面5个混入类并不是独立的视图,而是用于与其他基于类的视图结合使用
- 例如
APIView
、GenericAPIView
或ViewSet
。- 通过继承这些混入类和适当的视图,开发人员可以轻松地实现所需的功能,避免编写重复的代码。
ListCreateAPIView
- 继承自
GenericAPIView
、ListCreateModelMixin
,实现获取多条记录和创建记录的功能。
RetrieveUpdateDestroyAPIView
- 继承自
GenericAPIView
、RetrieveModelMixin
、UpdateModelMixin
、DestroyModelMixin
,实现获取单条记录、更新记录和删除记录的功能。
RetrieveDestroyAPIView
- 继承自
GenericAPIView
、RetrieveModelMixin
、DestroyModelMixin
,实现获取单条记录和删除记录的功能。
RetrieveUpdateAPIView
- 继承自
GenericAPIView
、RetrieveModelMixin
、UpdateModelMixin
,实现获取单条记录和更新记录的功能。
【4】视图集
- 视图集是一种组织和管理视图的方式,它包括了多个接口,并允许使用相同的 URL 前缀来映射这些接口。
-
ModelViewSet
- 继承自
GenericViewSet
和ModelMixin
,提供了常用的 CRUD 操作(创建、读取、更新和删除)。 - 5个接口
- 继承自
-
ReadOnlyModelViewSet
- 继承自
GenericViewSet
和ListModelMixin
,只提供读取操作(查询所有和查询单条)。 - 2个接口
- list和retrieve (查询所有和查询单条)
- 继承自
-
ViewSetMixin
- 是一个"魔法"混入类,不能单独使用,必须与视图类一起使用,用于改变路由的写法。
-
ViewSet
- 继承自
ViewSetMixin
和APIView
- 用于继承
APIView
并改变路由的写法 - 在视图类中的方法可以任意命名。
- 继承自
-
GenericViewSet
- 继承自
ViewSetMixin
和GenericAPIView
- 用于继承
GenericAPIView
并改变路由的写法 - 在视图类中的方法可以任意命名。
- 继承自
【5】图解各个视图类之间的关系
【1】总图
【2】视图类
【3】视图集
【6】补充和扩展
-
5 个视图扩展类(配合 GenericAPIView 使用):
-
这 5 个视图扩展类是根据公司规范封装的,并且必须配合 GenericAPIView 使用。
-
它们的功能是返回格式符合公司规范的数据。
-
示例:
pythonfrom rest_framework import generics class SuccessView(generics.GenericAPIView): def get(self, request): data = {'code': 100, 'msg': '成功'} return self.success_response(data)
-
-
9 个视图子类:
- 5 个视图扩展类 + GenericAPIView
- List 和 Create 的组合:
- 在 GenericAPIView 的基础上
- 结合 ListAPIView 和 CreateAPIView,实现同时支持列表展示和创建对象的功能。
- 其他组合:
- 根据具体需求,可以自由组合以上基类和其他扩展类来定制视图功能。
-
视图集(ViewSets):
- 视图集是将多个视图组合到一个类中,用于简化 URL 配置。以下是常见的视图集类别和特点:
- ViewSetMixin:
- 继承 ViewSetMixin 可以修改路由配置,默认使用动词方法名作为路由。
- ViewSet:
- 继承 ViewSetMixin 和 views.APIView,允许直接定义处理请求的方法。
- GenericViewSet:
- 继承 ViewSetMixin 和 generics.GenericAPIView,结合了通用视图和动态路由匹配。
- ModelViewSet:
- 继承 GenericViewSet 的基础上,提供了默认实现的增删改查功能。
- 重写 perform_create:在每次创建记录时执行特定操作。
- 序列化使用一个序列化类,反序列化使用配置的那个序列化类。
- 自定义方法:通过使用 action 装饰器,可以自定义额外的方法,这些方法的查询对象和序列化类与配置的不一样。
-
视图类:
-
在视图类中,self 内部有 request 对象和 action 属性。
-
request 对象表示当前请求,在视图类的方法中可通过 self.request 引用。
-
action 是指当前调用的方法字符串名。
-
示例:
pythonfrom rest_framework.views import APIView class MyView(APIView): def get(self, request): data = {'message': 'Hello, World!'} return Response(data)
-
【八】认证,频率,权限
-
认证:
- 创建一个自定义的类来继承Django框架中的BaseAuthentication。
- 重写authenticate方法来执行认证过程
- 如果认证成功,你可以返回一个包含用户和认证信息的元组;
- 如果认证失败,你可以选择抛出一个异常。
- 可以在全局或局部配置文件中设置认证方式
-
权限:
- 创建一个与BaseAuthentication相似的类,继承自它来进行权限控制。
- 重写其中的方法来定义你的权限逻辑
- 如果用户拥有足够的权限,】允许他们访问某些资源;反之,抛出一个异常或返回错误信息。
-
频率:
- 创建一个自定义的类来继承SimpleRateThrottle。
- 重写get_cache_key方法来确定在缓存中存储频率限制所用的键值。
- 这个键值可以基于不同的因素,比如IP地址或用户ID。
- 返回什么,就以什么做限制[ip,用户id]
- 类属性:
- 可以使用类属性scope来指定频率限制的范围。
- scope
- 对于频率限制,可以在全局或局部的配置文件中进行设置。
-
频率类:
- 可以创建一个自定义的类,并继承Django框架中的BaseThrottle。
- 在这个类中,你需要重写allow_request方法来确定是否允许请求。
- 如果请求的频率在设定的限制范围内,你可以返回True;否则,返回False。
【补充】自己写频率,同样的ip,一分钟只能访问3次---》频率类
自定义的逻辑
- (1)取出访问者ip
- (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
- (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间
- (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
- (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
import time
class CustomThrottle:
access_records = {} # 存储访问记录的字典,格式为{ip: [timestamp1, timestamp2, ...]}
def allow_request(self, request):
ip = self.get_client_ip(request)
if ip not in self.access_records:
# 如果当前ip不在访问记录字典中,则将其添加并返回True(第一次访问)
self.access_records[ip] = []
return True
# 保留60s内的访问记录,即删除超过60s的时间戳
current_time = int(time.time())
self.access_records[ip] = [timestamp for timestamp in self.access_records[ip]
if current_time - timestamp <= 60]
if len(self.access_records[ip]) < 3:
# 如果60s内的访问不足3次,则将当前时间戳插入到列表的第一个位置,返回True通过验证
self.access_records[ip].insert(0, current_time)
return True
# 如果60s内的访问超过3次,则返回False验证失败
return False
def get_client_ip(self, request):
"""
从请求中获取客户端IP地址
"""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
- 上述代码是一个自定义的频率限制类CustomThrottle,其中使用access_records字典来存储每个访问者的访问记录。
- allow_request方法用于判断是否允许当前请求进行访问。
- 现在,我将为你展示一个简单的案例来使用这个自定义的频率限制类:
throttle = CustomThrottle()
# 模拟访问情况
request1 = {'REMOTE_ADDR': '192.168.0.1'} # 第一次访问
print(throttle.allow_request(request1)) # True
request2 = {'REMOTE_ADDR': '192.168.0.1'} # 第二次访问(仅相隔1秒)
time.sleep(1)
print(throttle.allow_request(request2)) # True
request3 = {'REMOTE_ADDR': '192.168.0.2'} # 第一次访问
print(throttle.allow_request(request3)) # True
request4 = {'REMOTE_ADDR': '192.168.0.1'} # 第三次访问(相隔不到60s)
print(throttle.allow_request(request4)) # False,访问超过频率限制
request5 = {'REMOTE_ADDR': '192.168.0.1'} # 第四次访问(相隔60s)
time.sleep(60)
print(throttle.allow_request(request5)) # True,60s后访问通过验证
- 在上述案例中,我们首先创建了CustomThrottle的实例throttle,并模拟了一些请求情况。
- 根据这个频率限制逻辑,前三次访问都被允许通过,第四次访问由于超过频率限制而失败,但在等待60秒之后,第五次访问又通过了验证。
【九】排序
-
我们可以使用内置的排序功能来对查询结果进行排序。
- 为了实现排序功能,我们需要继承
GenericAPIView
类 - 并在视图类中配置
filter_backends=[OrderingFilter]
属性。
- 为了实现排序功能,我们需要继承
-
在视图类中,配置filter_backends=[OrderingFilter],还需要配置属性:ordering_fields=[id,price]
from rest_framework.generics import GenericAPIView
from rest_framework.filters import OrderingFilter
class MyView(GenericAPIView):
"""
示例视图类
"""
queryset = MyModel.objects.all() # 指定查询集
serializer_class = MySerializer # 指定序列化器
filter_backends = [OrderingFilter] # 配置排序过滤器
ordering_fields = ['id', 'price'] # 配置允许排序的字段
def get(self, request, *args, **kwargs):
"""
GET请求处理逻辑
"""
return self.list(request, *args, **kwargs)
- 在上述代码中,我们定义了一个名为
MyView
的视图类,并指定了查询集queryset
和序列化器serializer_class
。- 接下来,我们将
filter_backends
属性设置为[OrderingFilter]
,以启用排序功能。- 然后,通过
ordering_fields
属性指定了允许排序的字段,例如'id'
和'price'
。
-
如果是继承APIView写排序,自己写
-
【示例】
-
假设我们有一个模型
Car
表示汽车,具有id
和price
两个字段。- 我们将创建一个视图类来展示所有车辆并根据
price
字段进行排序。
- 我们将创建一个视图类来展示所有车辆并根据
-
以下是一个基于上述内容的案例代码:
pythonfrom rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.generics import get_object_or_404
from rest_framework.filters import OrderingFilter
from .serializers import CarSerializer
from .models import Car
class CarListView(APIView):
"""
汽车列表视图
"""
filter_backends = [OrderingFilter]
ordering_fields = ['price']
def get(self, request):
cars = Car.objects.all()
serializer = CarSerializer(cars, many=True)
return Response(serializer.data)
- 在上述代码中,我们创建了一个名为
CarListView
的视图类,并继承了APIView
。- 然后,我们配置了
filter_backends
属性为[OrderingFilter]
来启用排序功能,并指定了ordering_fields
属性为['price']
,以允许根据price
字段进行排序。
- 然后,我们配置了
- 在
get
方法中,我们查询所有汽车信息,并使用序列化器将结果序列化后返回。 - 这样,我们就可以通过发起GET请求,获取所有汽车列表并按照价格进行排序了。
【十】过滤
【1】简解
-
在Django中,我们可以使用内置的
SearchFilter
来进行模糊查询。 -
此外,还可以使用第三方库
django-filter
实现更强大的过滤功能。 -
如果需要自定义过滤逻辑,我们可以自己编写一个类,并继承
BaseFilterBackend
,然后重写filter_queryset
方法来实现过滤操作。
【2】步骤
-
内置过滤器
SearchFilter
(模糊查询):- 首先,导入相关模块和类:
from rest_framework.filters import SearchFilter
- 在视图类中配置
filter_backends=[SearchFilter]
,并设置search_fields
属性为要进行模糊查询的字段列表。例如:filter_backends = [SearchFilter] search_fields = ['name', 'description']
- 首先,导入相关模块和类:
-
第三方过滤器库
django-filter
:- 首先,安装
django-filter
库:pip install django-filter
- 在视图类中配置
filter_backends=[filters.DjangoFilterBackend]
,并设置filterset_class
属性为自定义的过滤器类。 - 编写自定义的过滤器类,并继承
filters.FilterSet
,在其中定义需要进行过滤的字段和过滤方式。
- 首先,安装
-
自定义过滤器类:
- 首先,导入相关模块和类:
from rest_framework.filters import BaseFilterBackend
- 编写自定义过滤器类,并继承
BaseFilterBackend
。 - 在自定义的过滤器类中重写
filter_queryset
方法,实现自定义的过滤逻辑,并返回过滤后的查询集对象。
- 首先,导入相关模块和类:
【3】示例
- 假设我们有一个模型
Product
表示商品,具有name
和description
两个字段。- 我们将创建一个视图类来展示所有商品,并根据
name
字段进行模糊查询。
- 我们将创建一个视图类来展示所有商品,并根据
- 以下是一个基于上述内容的案例代码:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.filters import SearchFilter
from .serializers import ProductSerializer
from .models import Product
class ProductListView(APIView):
"""
商品列表视图
"""
filter_backends = [SearchFilter]
search_fields = ['name'] # 设置要进行模糊查询的字段
def get(self, request):
query = request.GET.get('query') # 获取查询参数
products = Product.objects.all()
if query:
products = products.filter(name__icontains=query) # 使用模糊查询过滤商品
serializer = ProductSerializer(products, many=True)
return Response(serializer.data)
- 在上述代码中,我们创建了一个名为
ProductListView
的视图类,并继承了APIView
。- 然后,我们配置了
filter_backends
属性为[SearchFilter]
,以启用模糊查询功能,并通过search_fields
属性指定要进行模糊查询的字段,例如['name']
。
- 然后,我们配置了
- 在
get
方法中,我们获取查询参数query
,然后查询所有商品信息并存储在products
中。- 如果存在查询参数,我们将根据
name
字段使用icontains
进行模糊查询过滤。 - 最后,我们使用序列化器将过滤后的结果序列化并返回。
- 如果存在查询参数,我们将根据
【十一】分页
-
三种分页方式:
- PageNumberPagination
- 这是一种基于页码的分页方式。
- 它将结果集按照每页显示的数量进行分页,并提供了相关的类属性来控制分页效果,如
page_size
(每页显示的数量)、max_page_size
(最多显示的数量)等。 - 在视图类中继承
PageNumberPagination
,并在类属性中配置相关参数来控制分页效果。
- LimitOffsetPagination
- 这是一种基于偏移量的分页方式。
- 它通过指定偏移量和限制数量来获取结果集的片段,并提供了类属性来控制分页效果,如
default_limit
(默认每页显示的数量)、max_limit
(最多显示的数量)等。 - 在视图类中继承
LimitOffsetPagination
,并在类属性中配置相关参数来控制分页效果。
- CursorPagination
- 这是一种基于游标的分页方式。
- 它使用特定的游标值作为参考点来获取结果集,并提供了类属性来控制分页效果,如
cursor_query_param
(表示当前游标值的查询参数)等。 - 在视图类中继承
CursorPagination
,并在类属性中配置相关参数来控制分页效果。
- PageNumberPagination
-
每个有些类属性控制:每页显示多少条,最多显示多少条,第几页。。。
-
写个类,继承三个之一,配置在视图类上(必须继承GenericAPIView)
-
继承APIView写分页
【示例】
- 以下是一个基于上述内容的案例代码:
pythonfrom rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination
from .serializers import ProductSerializer
from .models import Product
class CustomPagination(PageNumberPagination):
"""
自定义分页类
"""
page_size = 10 # 每页显示的数量
max_page_size = 100 # 最多显示的数量
page_query_param = 'page' # 表示页码的查询参数
page_size_query_param = 'size' # 表示每页显示数量的查询参数
class ProductListView(APIView):
"""
商品列表视图
"""
pagination_class = CustomPagination
def get(self, request):
paginator = self.pagination_class()
products = Product.objects.all()
paginated_products = paginator.paginate_queryset(products, request)
serializer = ProductSerializer(paginated_products, many=True)
return paginator.get_paginated_response(serializer.data)
-
在上述代码中,我们创建了一个名为
CustomPagination
的自定义分页类,并继承了PageNumberPagination
。- 然后,在自定义分页类中配置了
page_size
(每页显示的数量)、max_page_size
(最多显示的数量)、page_query_param
(表示页码的查询参数)和page_size_query_param
(表示每页显示数量的查询参数)等相关参数。
- 然后,在自定义分页类中配置了
-
接下来,我们创建了一个名为
ProductListView
的视图类,并继承了APIView
。- 在该视图类中配置了
pagination_class
属性为我们自定义的分页类CustomPagination
。
- 在该视图类中配置了
-
在
get
方法中,我们初始化了分页器(paginator),然后查询所有商品信息并存储在products
中。- 接着,使用分页器的
paginate_queryset
方法对查询结果进行分页处理并获取分页后的商品列表(paginated_products
)。 - 最后,我们使用序列化器将分页后的结果序列化,并通过分页器的
get_paginated_response
方法返回分页的响应数据。
- 接着,使用分页器的
-
这样,我们就可以根据需求选择不同的分页方式,并在视图类中配置相应的分页类来实现分页功能。
【十二】异常处理
-
在Django REST Framework(DRF)中,可以通过全局异常处理来统一处理异常并返回一致的格式。
-
创建自定义的异常处理函数:
- 首先,我们需要创建一个函数来处理异常,并定义它的输入参数为
exc
和context
,其中exc
代表捕获的异常对象,context
表示异常发生的上下文信息。 - 在函数内部,调用DRF中默认的异常处理函数
exception_handler
,将exc
和context
作为参数传递给它。
- 首先,我们需要创建一个函数来处理异常,并定义它的输入参数为
-
处理异常并返回响应:
- 根据实际需求,在自定义的异常处理函数中对异常进行处理。
- 如果成功处理了异常且无需返回特定的响应内容,则返回
None
。 - 如果需要返回自定义的响应内容,可以通过创建一个
Response
对象,并设置相应的状态码、错误信息等。
-
配置全局异常处理函数:
- 在DRF的配置文件中,配置全局异常处理函数,以便在出现异常时能够调用该函数进行处理。
- 可以通过设置
EXCEPTION_HANDLER
参数,并指定为自定义的异常处理函数。
【示例】
以下是一个基于上述内容的案例代码:
pythonfrom rest_framework.views import exception_handler
from rest_framework.response import Response
def custom_exception_handler(exc, context):
"""
自定义全局异常处理函数
"""
# 调用DRF默认异常处理函数,获取默认的响应内容
response = exception_handler(exc, context)
if response is None:
# 如果异常未被处理且无需返回特定响应内容,则返回None
return None
# 自定义响应内容,可以根据实际需求进行组织
custom_response = {
'error_code': response.status_code,
'message': 'An error occurred while processing the request.',
'detail': str(exc),
}
# 创建自定义的响应对象
response.data = custom_response
return response
- 在上述代码中,我们创建了一个名为
custom_exception_handler
的自定义全局异常处理函数。- 在函数内部,我们首先调用DRF默认的异常处理函数
exception_handler
,将异常和上下文信息作为参数传递给它,并获取默认的响应内容。
- 在函数内部,我们首先调用DRF默认的异常处理函数
- 接着,我们对异常进行处理。如果成功处理了异常且无需返回特定的响应内容,我们直接返回
None
。- 如果需要返回自定义的响应内容,我们可以根据实际需求进行组织,并创建一个
Response
对象,将自定义的响应内容赋值给response.data
。
- 如果需要返回自定义的响应内容,我们可以根据实际需求进行组织,并创建一个
- 最后,我们将自定义的异常处理函数配置在DRF的配置文件中。
- 可以通过设置
EXCEPTION_HANDLER
参数,并指定为自定义的异常处理函数,以便在出现异常时能够调用该函数进行处理。
- 可以通过设置
【十三】接口文档编写
-
接口文档编写是一个重要的任务,而自动生成工具能够简化这个过程并提高效率。以下是对自动生成接口文档的概念进行详解:
-
使用CoRAPI(Code-Reading Assistant for API)等自动生成工具,可以根据代码的注释和结构生成接口文档。
- 这些工具通常会扫描项目中的代码,并解析注释中包含的特定标记和信息,以生成接口文档的相关内容。
- 通过使用这些工具,我们可以减少手动编写文档的工作量,并确保文档与实际代码一致性强。
-
下面是一个基于CoRAPI的自动生成接口文档的案例:
from django.shortcuts import render from django.http import JsonResponse def hello(request): """ 这个接口用于返回欢迎消息 """ message = "Hello, welcome to our API!" return JsonResponse({"message": message})
-
在上述代码中,我们定义了一个
hello
函数作为一个API接口,用于返回一个欢迎消息。函数内部的注释提供了对接口的描述。 -
使用CoRAPI等自动生成工具,在项目中执行相应的命令或配置,可以生成接口文档。根据我们的案例代码,生成的接口文档可能如下所示:
接口名称
/hello
描述
- 这个接口用于返回欢迎消息。
请求方法
GET
请求参数
该接口不需要请求参数。
响应
- 状态码:200 OK
- 返回类型:JSON
JSON示例
{
"message": "Hello, welcome to our API!"
}
- 在实际项目中,我们需要根据项目的具体情况和要求来配置和使用自动生成工具。
- 通常情况下,我们需要在代码中添加规范的注释,并运行相应的命令或配置工具以生成接口文档。
【十四】JWT认证
-
JWT(JSON Web Token)是一种用于身份认证和授权的开放标准。
- 它由三部分组成,即Header、Payload和Signature,通常以Base64编码的形式传输。
-
JWT是什么
- JWT是一种基于JSON的安全令牌,用于在客户端和服务器之间传输信息。
- JWT可以通过数字签名保证传输的信息不被篡改,并且可以使用密钥进行验证和解码。
-
三段式
- Header:包含算法类型和令牌类型等信息。
- Payload:包含自定义的用户信息或声明等。
- Signature:根据Header和Payload以及密钥生成的签名,用于验证令牌的完整性。
-
开发重点:签发,认证
- 签发:在用户认证成功后,将用户信息和其他需要的声明信息生成JWT并返回给客户端。
- 认证:客户端将JWT发送给服务器,在服务器验证JWT的合法性并解析出其中的用户信息进行认证和授权。
-
使用djangorestframework-jwt实现快速签发和认证:
- 基于auth的user表快速签发:
- djangorestframework-jwt提供了
ObtainJSONWebToken
视图,可以基于Django的内置User
模型直接进行用户认证并签发JWT。
- djangorestframework-jwt提供了
- 基于内置的认证类认证,配合权限:
- djangorestframework-jwt还可以与Django的内置认证类和权限系统结合使用,进行JWT的验证和用户权限的控制。
- 基于auth的user表快速签发:
-
登录成功返回格式
-
自定义用户表 -> 签发 -> 登录接口:
-
在自定义用户表中,可以添加额外的字段用于存储用户信息。
-
在用户登录成功后,根据用户信息生成JWT,并将其加入到响应中返回给客户端。
-
响应的格式可以自定义,一般包含JWT信息和其他用户相关信息。
from rest_framework_jwt.settings import api_settings def generate_jwt(user): jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) return token # Login function def login(request): # 用户验证逻辑 # ... user = User.objects.get(username=username) token = generate_jwt(user) response_data = { 'token': token, 'username': user.username, 'email': user.email, # 其他用户相关信息 } return JsonResponse(response_data)
-
-
自定义用户表 -> 认证 -> 认证类:
-
在自定义用户表中,可以添加额外的字段用于存储用户信息。
-
使用自定义的认证类进行用户认证,验证通过后生成JWT。
-
认证类可以与Django的认证机制结合使用,例如配合使用
AuthenticationBackend
类来实现多方式登录(例如用户名密码登录、手机号码验证码登录等)。from django.contrib.auth.backends import BaseBackend from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication class CustomAuthenticationBackend(BaseBackend): def authenticate(self, request, username=None, password=None): # 实现自定义的认证逻辑 # ... user = User.objects.get(username=username) return user # settings.py AUTHENTICATION_BACKENDS = [ 'my_app.backends.CustomAuthenticationBackend', ] JWT_AUTH = { 'JWT_SECRET_KEY': SECRET_KEY, 'JWT_AUTH_HEADER_PREFIX': 'Bearer', } # Login function def login(request): # 用户验证逻辑 # ... user = CustomAuthenticationBackend().authenticate(request, username=username, password=password) if user: token = generate_jwt(user) response_data = { 'token': token, 'username': user.username, 'email': user.email, # 其他用户相关信息 } return JsonResponse(response_data)
-
-
-
扩写了auth的user表
- 快速签发---》只能使用用户名密码,不能多方式登录
- 自定义登录接口---》实现多方式登录
【示例】
- 以下是一个简单的示例代码,展示了如何使用djangorestframework-jwt进行用户登录和JWT认证:
python# 安装djangorestframework-jwt: pip install djangorestframework-jwt
# serializers.py
from rest_framework import serializers
class UserSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework_jwt.settings import api_settings
class LoginView(APIView):
def post(self, request):
serializer = UserSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
# 在此处进行自定义用户表的验证逻辑
# ...
# 在验证通过后签发JWT
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(serializer.validated_data)
token = jwt_encode_handler(payload)
return Response({"token": token})
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
],
}
JWT_AUTH = {
'JWT_SECRET_KEY': SECRET_KEY,
'JWT_AUTH_HEADER_PREFIX': 'Bearer',
}
- 在上述代码中,通过自定义的
UserSerializer
对用户输入进行校验,并在LoginView
中进行用户的认证和JWT签发。- 在
settings.py
中配置了使用JWT_AUTH
来处理JWT的认证。
- 在
- 登录成功后,将返回一个包含JWT的响应,例如:
{"token": "xxxxxxxxxxx"}
。
【十五】自动注册路由的两种方式
第一步:导入一个路由类
from rest_framework.routers import SimpleRouter
- 两个路由类
- SimpleRouter
- DefaultRouter
第二步:实例化得到对象
router = SimpleRouter()
第三步:注册路由(视图类)
router.register("books", BookView, "books")
# router.register("自定义前缀", 需要注册视图类, "别名")
第四步:加入到路由中
方式一
- 将路由对象的URL配置添加到现有的
urlpatterns
中。 - 使用
urlpatterns += router.urls
将路由对象的URL配置列表添加到现有的URL配置中。
from django.urls import path
urlpatterns = [
...
]
urlpatterns += router.urls
print("urlpatterns:>>>", urlpatterns)
# urlpatterns:>>> [<URLResolver <URLPattern list> (admin:admin) 'admin/'>, <URLPattern 'book/'>]
print("router:>>>", router)
# router:>>> <rest_framework.routers.SimpleRouter object at 0x000001EB24D63130>
方式二
- 使用
include()
函数创建URL配置 - 并将路由对象作为参数传递给
include()
函数。 - 例如,使用
path("api/v1/", include(router.urls))
将路由对象的URL配置嵌套在前缀为"api/v1/"的URL路径下。
from django.urls import path, include
urlpatterns = [
path("api/v1/", include(router.urls))
]
【补充】序列化类源码分析之many参数的作用
-
如果传了many=True,序列化多条
- 得到的对象不是BookSerializer对象
- 而是ListSerialzier的对象中套了一个个的BookSerializer对象
-
如果传了many=False,序列化单条
- 得到的对象就是BookSerializer的对象
【补充】类实例化得到对象,是谁控制的
def __new__(cls, *args, **kwargs):
if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs)
return super().__new__(cls, *args, **kwargs)
在类实例化会触发,它比
__init__
早
__new__
造出裸体的人__init__
给裸体的人穿衣服