drf—CBV源码分析、APIView执行流程分析
部分补充知识—函数和方法
函数和方法:
函数:使用def关键字定义的函数,有几个参数就传几个参数,不能多,不能少——按位置传,按关键字传
方法:定义在类的内部,可以自动传值的函数,称为方法——绑定给对象的方法(对象方法),绑定给类的方法(类 方法)
对象方法:对象来调用,自动把对象传入,类可以调用,但是类调用就变成了普通函数,有几个值就传几个值。
类方法:类来调用,自动把类传入。对象也可以调用,内部就会取到对象的类,自动传入,也是方法。
函数和方法的区别:能否自动传值。
print(isinstance(add, FunctionType)) # 判断一个对象是不是这个类的对象 print(isinstance(add, MethodType)) # 判断一个对象是不是这个类的对象 class Foo: def run(self): pass @classmethod def xx(cls): pass @staticmethod def zz(): pass # 对象调用 run ,run就是方法 会自动传值 f=Foo()
#对象调用方法,就是方法,类调用方法就是普通函数
print(isinstance(f.run,FunctionType)) #T
print(isinstance(f.run,MethodType))
# 类来调用run,run就是函数,有几个值就要传几个值
print(isinstance(Foo.run,FunctionType)) #T
print(isinstance(Foo.run,MethodType))
#类的绑定方法,无论是类还是方法调用,就都是方法
# 类调用类的绑定方法---》就是方法
print(isinstance(Foo.xx,FunctionType))
print(isinstance(Foo.xx,MethodType)) #T
# 对象调用类的绑定方法---》 也是方法
print(isinstance(f.xx,FunctionType))
print(isinstance(f.xx,MethodType)) #T
#
#静态方法,无论谁调用,都是函数
# 对象来调用
print(isinstance(f.zz,FunctionType)) # True
print(isinstance(f.zz,MethodType)) #False
# 类来调用
print(isinstance(Foo.zz,FunctionType)) # True
print(isinstance(Foo.zz,MethodType)) #False
cbv源码分析
写cbv的流程:它是基于类的视图,使用类来编写,在类中写跟请求方式同名的方法,进行路由配置 :类名.as_view()(request) ,请求过来后,什么请求就会执行跟请求方式同名的方法。
cbv执行流程: 请求进入——进行路由匹配——如果匹配到对应的路由,内部就会执行XXXview.as_view()(request),因为XXXview中没有as_view()这个方法,所以去它的父类View中找,内部开始执行,View的as_view内部的view闭包函数,闭包函数执行了self.dispatch,这里的self是在XXXview类实例化的对象,在self.dispatch中通过反射找到同名的方法,然后执行,把request传入。
# View.as_view的源码 @classonlymethod def as_view(cls, **initkwargs): # 闭包函数 def view(request, *args, **kwargs): self = cls(**initkwargs) # 类实例化得到对象:BookView的对象 # 调用对象的绑定方法 dispath---》去BookView中找dispatch--》找不到去父类---》View中的dispatch return self.dispatch(request, *args, **kwargs) # 对象的方法 return view # 执行:BookView.as_view()(request)---》本质是执行 View中as_view内部的view方法,传入了reuqest----》在执行View的dispatch方法,传入了request # 看 :View.dispatch---》源码 def dispatch(self, request, *args, **kwargs): if request.method.lower() in self.http_method_names: # 判断请求方式是否在那个固定的列表中[get, post...]---> # 反射:通过字符串 动态的 操作对象的属性或方法 # 假设是get请求---》去BookView中取出 get方法赋值给handler handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed # 执行handler加括号,传入了request # 本质是执行 get(request) return handler(request, *args, **kwargs)
APIView执行流程分析
基于def的APIView写接口
1、使用drf,以后都写cbv,继承一个视图类,以后都继承def提供的APIView
2、APIView继承了django的View
3 之前的视图层导入view补充:这三个都是一个
from django.views import View
from django.views.generic import View
from django.views.generic.base import View
4、继承APIView写cbv,执行起来和之前效果一样,内部发生了很大变化
1 写视图类 from rest_framework.views import APIView class PublishView(APIView): def get(self, request): return JsonResponse({'code': 999}) 2 配置路由 path('publish/', PublishView.as_view()),
APIView的执行流程分析
比继承django的View多了:
1、去除了csrf认证
2、包装了新的request
3、执行了认证、频率、权限 三大认证
4、全局异常处理:在视图类的方法中执行报错,会异常捕捉,做统一处理
执行流程分析 -请求来了---》执行PublishView.as_view()(request)--->PublishView类没有as_view---》找父类---》APIView的as_view -APIView的as_view---》本质在禁用csrf认证 @classmethod def as_view(cls, **initkwargs): # 调用父类的as_view得到返回值赋值给view---》django的View的as_view【看过】---》返回View的as_view的闭包函数 view view = super().as_view(**initkwargs) # 现在这个view就是原来看的View的as_view的view # 去除了csrf认证----》局部禁用csrf---》在视图函数上加 装饰器csrf_exempt # 加了装饰器的本质: 被装饰的函数=装饰器(被装饰的函数) return csrf_exempt(view) -PublishView.as_view()(request)--》执行了禁用掉csrf的View的as_view的view---》self.dispath--->APIView的dispath def dispatch(self, request, *args, **kwargs): # 1 包装了新的request对象 request = self.initialize_request(request, *args, **kwargs) # 把新的request,赋值给了 self.request # self 是 PublishView的对象 self.request = request try: # 2 执行了3大认证 self.initial(request, *args, **kwargs) ######开始### 跟之前View的dispath是一样的---》根据请求方式执行类中同名方法 ###执行视图类的方法 if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) ######结束### # 执行三大认证和视图类的方法,如果出了异常,抛了错,会被捕获,统一处理 except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
装饰器本质:
@auth # add=auth(add)-->以后调用add 实际上调用 auth(add)() def add(): pass add=auth(add)
包装新的request
1、request.data:前端传入数据(post,put,编码格式),写在请求体中的数据都用
2、老request中有request.GET,新的使用request.query_params充当
3、其他属性方法,和之前一样
三大认证:
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
总结:
视图类继承了APIView后多了
1、去除了csrf认证
2、新的request
-request.data(前端传入的数据(post,put,编码格式)--->写到在请求体中的数据,都用,如果是urlencoded:QueryDict就是原来的request.POST,如果是json:就是普通字典---》这里都当字典用)
-request.query_params(老request中有 request.GET 新的使用request.query_params 充当)
-request.其他跟之前一样
-request._request 是老的request
3、三大认证(认证、权限、频率)
4、全局异常(全局异常处理:在视图类的方法中执行报错,会被异常捕获,做统一处理)
序列化组件介绍
借助于def提供的序列化组件快速完成序列化
步骤:
1、在配置文件中注册(浏览器可以显示出来)
INSTALLED_APPS = [ 'rest_framework', ]
2、写一个序列化类 --->新建一个py文件--->serializer.py
继承def提供的serializers.Serializer
在类中写要序列化的字段:字段类---》跟之前学过的models.py中的字段类完全对应,但是比models多
3、在视图类中,使用序列化类
传了两个参数:instance 要序列化的对象(queryset,单个对象)
many = True表示序列化多条,如果不写就是序列化一条
多条:serializer=UserSerializer(instance=users,many=True)
单条:serializer=UserSerializer(instance=user)
4、拿到序列化后的数据
serializer.data 多条是是列表,单挑是字典
5 、使用drf提供的Resposne 返回
from rest_framework.response import Response
序列化组件快速使用之序列化
路由
urlpatterns = [ path('users/', UserView.as_view()), path('users/<int:pk>', UserDetailView.as_view()), ]
视图类
from .models import User from .serializer import UserSerializer from rest_framework.response import Response class UserView(APIView): def get(self,request): users=User.objects.all() # 之前用for循环,现在用序列化类 # 传了两个参数:instance 要序列化的对象(qs,单个对象) many=True表示序列化多条,如果不写就是序列化一条 ser=UserSerializer(instance=users,many=True) # 拿到序列化后的数据 ser.data--->多条就是列表 单条字典 return Response(ser.data) class UserDetailView(APIView): def get(self,request,pk): user=User.objects.all().filter(pk=pk).first() # 传了两个参数:instance 要序列化的对象 many=True表示序列化多条 ser=UserSerializer(instance=user) return Response(ser.data)
序列化类
# 写序列化类 from rest_framework import serializers class UserSerializer(serializers.Serializer): # 写要序列化的字段 name = serializers.CharField() # hobby = serializers.CharField() # password=serializers.CharField() age=serializers.IntegerField()
常用字段和参数
常用字段类
写序列化类的时候,写了CharField,IntegerField 跟django中models中的类似
序列化中的和models中的一一对应,但是序列化类中多一些
多的有(ListField、DictField)
BooleanField() | |
---|---|
NullBooleanField | NullBooleanField() |
CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) |
EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) |
RegexField | RegexField(regex, max_length=None, min_length=None, allow_blank=False) |
SlugField | SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+ |
URLField | URLField(max_length=200, min_length=None, allow_blank=False) |
UUIDField | UUIDField(format=’hex_verbose’) format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" |
IPAddressField | IPAddressField(protocol=’both’, unpack_ipv4=False, **options) |
IntegerField | IntegerField(max_value=None, min_value=None) |
FloatField | FloatField(max_value=None, min_value=None) |
DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置 |
DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) |
DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) |
TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) |
DurationField | DurationField() |
ChoiceField | ChoiceField(choices) choices与Django的用法相同 |
MultipleChoiceField | MultipleChoiceField(choices) |
FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ListField | ListField(child=, min_length=None, max_length=None) |
DictField |
常用字段参数
字段类上,可以传参数,是做反序列化校验用的
CharField:max_length,min_lenght,allow_blank: 可以不传
IntegerField:max_value,min_value
所有字段都可以用通用的
非常重要:read_only,write_only
default,required,allow_null
最大长度 | |
---|---|
min_lenght | 最小长度 |
allow_blank | 是否允许为空 |
trim_whitespace | 是否截断空白字符 |
max_value | 最小值 |
min_value |
参数名称 | 说明 |
---|---|
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
required | 表明该字段在反序列化时必须输入,默认True |
default | 反序列化时使用的默认值 |
allow_null | 表明该字段是否允许传入None,默认False |
validators | 该字段使用的验证器 |
error_messages | 包含错误编号与错误信息的字典 |
label | 用于HTML展示API页面时,显示的字段名称 |
help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 |
序列化组件之校验
序列化组件
序列化、反序列化、反序列化校验
反序列化之校验 ser.is_valid()
1、字段自己的校验规则(字段类的属性上)
2、局部钩子(给某个字段加上校验规则)
3、全局钩子(多个字段联合起来)
反序列化保存
ser.save()---》必须序列化类中重写 create--》自己定保存到哪个表
def create(self, validated_data): # validated_data:前端传入,校验过后的数据
user = User.objects.create(**validated_data)
return user
ser = UserSerializer(data=request.data) #反序列化的时候这样写
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能