反序列化类校验部分源码解析,断言,drf之请求与响应
反序列化类校验部分源码解析
- 反序列化校验,什么时候开始执行校验(切入点)
- 视图类中的 ser.is_valid(),就会执行校验,校验通过返回True,不通过返回False
入口:ser.is_valid()
ser是序列化类的对象,假设序列化类是BookSerializer---》我们在它的内部找is_valid---》找不到,找到父类的父类BaseSerializer中有is_valid :【raise_exception:先注意,它的作用就是替代视图层中的if判断,当我们的程序运行is_valid的结果时False的时候会直接抛出异常】
def is_valid(self, *, raise_exception=False): if not hasattr(self, '_validated_data'): try: # self序列化类的对象,属性中没有_validated_data,一定会走这句【核心】 self._validated_data = self.run_validation(self.initial_data) except ValidationError as exc: self._validated_data = {} self._errors = exc.detail else: self._errors = {} if self._errors and raise_exception: raise ValidationError(self.errors) return not bool(self._errors) '上面的代码我们简单区分一下可以分成两块,断言部分可以不管,当没有_validated_data这个属性或方法的时候会对他进行异常捕获。另一部分就是添加errors的错误信息并报错。
通过这里我们可以猜到,执行序列化功能的代码肯定就是异常捕获那里了,而他的代码中运行了一个run_validation,这就是我们要研究的目标' # self._validated_data = self.run_validation(self.initial_data) 核心--》self序列化类的对象
切记一定不要按住ctrl键点击,直接点会去Fields.py文件中的run_validation.
图解
-真正的执行顺序是,从下往上找,对象中找不到,再往父类上一层层找
-最终从Serializer类中找到了run_validation,而不是Fields.py文件中找到的run_validation
局部钩子与全局钩子
def run_validation(self, data=empty): # 字段自己的,validates方法(就是执行字段中的约束条件,对他们进行校验) (is_empty_value, data) = self.validate_empty_values(data) if is_empty_value: return data # 局部钩子----【局部钩子】(ctrl点进来后我们可以发现他的代码中有反射,用getattr去找validate_开头的方法,回顾一下drf中的局部钩子,两者命名格式一样,因此这里就是局部钩子) value = self.to_internal_value(data) try: self.run_validators(value) # 全局钩子--》如果在BookSerializer中写了validate,优先走它,非常简单,ctril点进去我们可以看到他其实啥都没做就把数据返回出去了,但是当我们自定义后,就会现用我们定义的全局钩子进行校验 value = self.validate(value) except (ValidationError, DjangoValidationError) as exc: raise ValidationError(detail=as_serializer_error(exc)) return value # 局部钩子 self.to_internal_value(data) ---》self是BookSerializer的对象,从根上找 def to_internal_value(self, data): ret = OrderedDict() errors = OrderedDict() fields = self._writable_fields # fields写在序列化类中一个个字段类的对象,即我们常说的字段名称 for field in fields: # self BookSerializer的对象,反射validate_name validate_method = getattr(self, 'validate_' + field.field_name, None) try: # 在执行BookSerializer类中的validate_name方法,传入了要校验的数据 validated_value = validate_method(validated_value) except ValidationError as exc: errors[field.field_name] = exc.detail else: set_value(ret, field.source_attrs, validated_value) if errors: raise ValidationError(errors) return ret
图解
局部钩子:
全局钩子:
总结:
is_valid---》BaseSerializer的is_valid--》执行了self.run_validation(self.initial_data)---》Serializer的run_validation---》self.to_internal_value(data):局部钩子 或者 value = self.validate(value) :全局钩子 self.to_internal_value(data):局部钩子----》getattr(self, 'validate_' + field.field_name, None)
断言
# 我们可以发现源码中大量使用try和断言 # 关键字assert ,有什么作用? # 我断定你是xx,如果不是就抛异常 name = 'xxx' # if name == 'xxx': # print('对了') # else: # # print('错了') # raise Exception('名字不为lqz,不能继续走了') assert name=='xxx' # 断定是,如果不是,就抛异常 print('后续代码')
Request能够解析的前端传入的编码格式
需求:该接口只能接收json格式,不能接收其他格式
方式一:在继承自APIView及其子类的的视图类中配置(局部配置)
# 总共有三个:from rest_framework.parsers import JSONParser,FormParser,MultiPartParser class BookView(APIView): parser_classes = [JSONParser,]
'我们在APIView中可以看到一个属性叫做parser_classes,ctrl点击他后面的方法DEFAULT_PARSER_CLASSES,我们可以看到并不能直接点进来,
但是这里我们可以去rest_framework的配置文件中找,找到一个叫settings.py的文件就能在内部找到DEFAULT_PARSER_CLASSES,它的使用当时也是类似我们配置文件中的注册,'
方式二:在配置文件中配置(影响所有,全局配置)
-django有套默认配置,每个项目有个配置 -drf有套默认配置,每个项目也有个配置---》就在django的配置文件中 REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES': [ # 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', # 'rest_framework.parsers.MultiPartParser', ], }
方式三:全局配了1个,某个视图类想要3个,怎么配?
- 只需要在视图类,配置3个即可
- 先从视图类自身找,找不到,去项目的drf配置中找,再找不到,去drf默认的配置找
常用参数
- data
POST、PUT、PATCH请求方式解析后的数据。(原生django的PUT请求在request.POST中取不到。)
- query_params
与原生的GET一样。
- 其他的方法和原来的request使用方法一致:
底层原理:在Request实例化对象时,self._request = request,将原来的request对象给了Request的对象,又在Request类中定义了__getattr__魔法方法,当在视图函数中获取request对象的属性和方法时,找不到会触发魔法方法的执行,利用反射获取原来的request对象中的方法。
def __getattr__(self, attr): """ If an attribute does not exist on this instance, then we also attempt to proxy it to the underlying HttpRequest object. """ try: return getattr(self._request, attr) except AttributeError: return self.__getattribute__(attr)
Response类的实例化参数
- data:列表或者字典,序列化成json字符串返回给前端
- status:响应状态码,默认是200:from rest_framework.status import HTTP_200_OK
- headers :响应头,以字典的形式返回给前端
- content_type :响应的编码格式
- template_name :指定模板
class Test(APIView): def get(self, request): return Response(data={}, status=status.HTTP_201_CREATED, headers={'name': 'xxx'})
drf之响应
Response能够响应的编码格式
# drf 是djagno的一个app,所以要注册,不注册的话不能用浏览器访问接口 # drf的响应,如果使用浏览器和postman访问同一个接口,返回格式是不一样的(浏览器会报错,postman返回数据) -drf做了个判断,如果是浏览器,好看一些,如果是postman只要json数据 # 方式一:在视图类中写(局部配置) -两个响应类---》找---》drf的配置文件中找--》两个类(也是在rest_framework的settings.py文件中) -from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer class BookView(APIView): renderer_classes=[JSONRenderer,] # 方式二:在项目配置文件中写(全局配置) REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': [ 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ], } # 方式三:使用顺序(一般就用内置的即可) 优先使用视图类中的配置,其次使用项目配置文件中的配置,最后使用内置的
Resposne的源码属性或方法
# drf 的Response 源码分析 -from rest_framework.response import Response -视图类的方法返回时,retrun Response ,走它的__init__,init中可以传什么参数 # Response init可以传的参数 def __init__(self, data=None, status=None, template_name=None, headers=None, exception=False, content_type=None) -data:之前咱们写的ser.data 可以是字典或列表,字符串---》序列化后返回给前端---》前端在响应体中看到的就是这个 -status:http响应的状态码,默认是200,你可以改 -drf在status包下,把所有http响应状态码都写了一遍,常量 -from rest_framework.status import HTTP_200_OK -Response('dddd',status=status.HTTP_200_OK) -template_name:了解即可,修改响应模板的样子,BrowsableAPIRenderer定死的样子,后期公司可以自己定制 -headers:响应头,http响应的响应头 -考你,原生djagno,如何像响应头中加东西 # 四件套 render,redirect,HttpResponse,JsonResponse obj = HttpResponse('dddd') obj['xxc'] = 'yyc' return obj -content_type :响应编码格式,一般不动 # 重点:data,status,headers