APIView执行流程分析
1 在路由中:path('books/', views.BookView.as_view()),请求来了 # 2 先看 as_view()---->APIView的 as_view---》as_view执行结果跟之前一样,去除了csrf认证 @classmethod def as_view(cls, **initkwargs): view = super().as_view(**initkwargs) # 调用父类的 as_view,view还是View的as_view # 以后所有请求,都不会做csrf认证了 return csrf_exempt(view) # 3 请求来了执行 views.BookView.as_view()(request)--->view(request)--->csrf_exempt(view)(request)--->内部核心---》return self.dispatch(request) # 4 self 是 APIView类的对象---》APIView没有dispatch---》APIView的dispatch,核心代码如下 def dispatch(self, request, *args, **kwargs): # 后续的request都是 initialize_request 返回结果--》新的request--》drf的Requet类的对象 request = self.initialize_request(request, *args, **kwargs) # 新的request放到了 self.request中---》self是BookView类的对象 # 后续视图类的方法中 可以直接 self.request取出 当次请求的request对象 self.request = request try: # 执行了三大认证: ''' self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request) ''' self.initial(request, *args, **kwargs) ###### 通过反射,去视图类中:BookView中执行跟请求方式同名的方法 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 # request是新的Request类的对象了 get方法的第一个参数request也是新的 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 # 5 看self.initialize_request 是APIView的 def initialize_request(self, request, *args, **kwargs): # 类实例化得到对象,传入一些参数 # Request类--》drf提供的类 from rest_framework.request import Request return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context ) # 只要继承APIView,以后方法中得request都变了,成了 rest_framework.request.Request 的对象了 但是用起来,跟之前的一模一样 # 原来的是:django.core.handlers.wsgi.WSGIRequest 类的对象 # 总结: 1 以后视图类方法中得request对象,变成了新的request,它是rest_framework.request.Request 的对象了,但是用起来跟之前一样 2 把新的request对象,同时放到了 视图类的对象中 self.request = request 后续从视图类中可以直接通过 self.request取出来 3 在执行视图类的方法之前,执行了三大认证 4 如果三大认证或视图类的方法执行出错,会有全局异常处理 5 以后所有的接口都去除了csrf认证
回顾装饰器
装饰器作用:
在不改变原函数代码和调用方式的基础上,为他增加新功能
import time def add(a, b): time.sleep(1) return a + b #### 统计add的运行时间 # start_time = time.time() # add(3, 4) # end_time = time.time() # print('运行时间是:', end_time - start_time) ### 写一个通用的,以后只要统计任意函数运行时间,都使用通用的--->改变了调用方式,不行 # def outer(func): # start_time = time.time() # func(3, 4) # end_time = time.time() # print('运行时间是:', end_time - start_time) # # outer(add) def outer(func): def inner(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) end_time = time.time() print('运行时间是:', end_time - start_time) return res return inner # inner=outer(add) # 调用inner,就是执行,inner内部包了 add函数,inner叫闭包函数 # inner() # 改变了调用方式 # 继续优化 add = outer(add) res = add(3, 4) # add现在本质调用 inner,参数传给 inner,inner要能接收a,b 并且有返回结果 print(res) # 以后想装饰哪个函数 得按如下操作,麻烦 # def ee(): # print('ee') # # ee=outer(ee) # ee() # python提供了一个语法糖 @ ,以后只要按如下方式编写代码,就能实现上述功能 @outer # 等同于 ee=outer(ee) 该语法糖会把被装饰器的函数ee,当做参数,传入outer,并且把outer的执行结果,返回赋值给ee def ee(): print('ee') # 以后ee其实已经不是ee了,是inner了,执行ee本质在执行inner #####上述是原理#### ## 记忆 # 以后写它 def outer(func): def inner(*args, **kwargs): # 被装饰器函数,执行之前干事 res = func(*args, **kwargs) #被装饰函数,执行之后干事情 return res return inner # 装饰某个函数 @outer def add(a,b): time.sleep(1) return a+b
APIView再回顾
在执行视图类的方法之前 (
去除 csrf
包装新的 request
在视图类中加入了 self.request
执行三大认证
)
在执行视图类的方法之后 干了一些事
处理全局异常
本质就是做了 装饰器的功能
Request对象分析
分析APIVIew时,分析出,以后request都是新的request了,是drf提供的Request的对象 from rest_framework.request import Request # 源码解析之 __init__--->老的request在新的内部---》request._request: -先看 __init__--->类实例化得到对象时,对对象进行初始化,往对象中放数据 def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None): # 传入的request是老的,django原生的request # 放到了self._request,self 是新的request类的对象 self._request = request self._data = Empty self._files = Empty -什么时候调用的 __init__? 新的 老的 -request = self.initialize_request(request, *args, **kwargs) 老的 return Request(request) # 以后用新的跟用老的一样,为什么? 新的 requet.method 新的 request.path # 魔法方法: 在类内部,以 __开头 __结尾的方法, 在某种情况下会自动调用,他们称之为魔法方法 学过:__init__: 类名() 自动触发 __str__: print(对象) 自动触发 还有哪些? 很多---》所有类都继承object类---》它都在object类中 # 今天要学的 __getattr__ -对象.属性 ,属性不存在会触发 # 回头看 新的 requet.method用的时候,如果method不存在就会触发 Request类的 __getattr__ # 源码解析之 __getattr__ -逻辑肯定是:从老的request中取出,你想要的东西 def __getattr__(self, attr): try: # 通过反射,去老的中取,能取到就返回,取不到,执行except代码,再取不到就报错 return getattr(self._request, attr) except AttributeError: return self.__getattribute__(attr) # 以后新的request中多了个属性 data---》前端post,put提交的请求体中得数据,都会放在request.data中,无论何种编码格式,它都是字典 ######## 总结 -1 新的request中有老的requet, 在request._request -2 新的request 多了data属性,客户端提交的请求体中得数据,无论以那种方式编码,都在request.data中 -3 其他的使用,跟之前老request一模一样 request.method request.path request.POST request.GET request.FILES 。。。 # 总结: - 1 原生django--》post--》提交数据,只能处理urlencoded和form-data编码,从request.POST中取 - 2 原生djagno--》put--》提交数据,处理不了,需要我们自己从body中取出来处理 -分不同编码格式: urlencoded ---》name=lqz&age=19-->字符串切割 json----》{"xxz":"xx","yyz":"yyy"}---》json.loads -3 原生django不能处理json提交的数据,需要自己做(put,post) json----》body中:{"xxz":"xx","yyz":"yyy"}---》json.loads -4 新的request解决了所有问题 request.data
序列化类介绍
序列化类(组件)可以干的事
1 序列化 qs对象,单个对象 做序列化给前端
2 反序列化数据校验:前端传入数据---》校验数据是否合法
3 反序列化---》前端传入数据,存到数据库中
继承APIView+Response实现 Publish的5个接口 class PublishView(APIView): def get(self, request): publish_list = Publish.objects.all() l = [] for publish in publish_list: l.append({'name': publish.name, 'addr': publish.addr}) return Response({'code': 100, 'msg': '查询所有成功', 'results': l}) def post(self, request): # 如果是urlencoded编码,这种方式不行 publish = Publish.objects.create(**request.data) publish = Publish.objects.create(name=request.data.get('name'), addr=request.data.get('addr')) return Response({'code': 100, 'msg': '新增成功', 'results': {'name': publish.name, 'addr': publish.addr}}) class PublishDetailView(APIView): def get(self, request, pk): publish = Publish.objects.filter(pk=pk).first() return Response( {'code': 100, 'msg': '查询单条成功', 'results': {'name': publish.name, 'addr': publish.addr}}) def put(self, request, pk): publish = Publish.objects.filter(pk=pk).first() publish.name = request.data.get('name') publish.addr = request.data.get('addr') publish.save() return Response({'code': 100, 'msg': '修改成功', 'result': {'name': publish.name, 'addr': publish.addr}}) def delete(self, request, pk): Publish.objects.filter(pk=pk).delete() return Response({'code': 100, 'msg': '删除成功'})
序列化类的使用
上面做 序列化 是自己for循环拼的
反序列化,自己写的
缺少 数据校验
——————drf提供的序列化器,实现序列化, 反序列化 和 数据校验
使用步骤:
1 写个py文件,叫serializer.py
2 写个类,继承serializers.Serializer
3 在类中写要序列化的字段
class PublishSerializer(serializers.Serializer): # 写字段,要序列化的字段 name = serializers.CharField() addr = serializers.CharField() id = serializers.IntegerField() 4 在视图类中使用,完成 序列化 -多条 -ser = PublishSerializer(instance=publish_list, many=True) -ser.data 序列化后的数据 -单条: -ser = PublishSerializer(instance=publish) -ser.data 序列化后的数据
序列化类快速使用
视图类
from .serializer import PublishSerializer class PublishView(APIView): def get(self, request): publish_list = Publish.objects.all() ser = PublishSerializer(instance=publish_list, many=True) # 如果序列化多条,要many=True return Response({'code': 100, 'msg': '查询所有成功', 'results': ser.data}) class PublishDetailView(APIView): def get(self, request, pk): publish = Publish.objects.filter(pk=pk).first() ser = PublishSerializer(instance=publish) # 单个不写many=True return Response( {'code': 100, 'msg': '查询单条成功', 'results': ser.data})
序列化类
from rest_framework import serializers class PublishSerializer(serializers.Serializer): # 写字段,要序列化的字段 name = serializers.CharField() # addr = serializers.CharField() id = serializers.IntegerField()
路由
urlpatterns = [ path('publish/', views.PublishView.as_view()), path('publish/<int:pk>', views.PublishDetailView.as_view()), ]
序列化类反序列化校验
序列化类可以做字段校验---》三层 -第一层:字段自己的serializers.CharField(max_length=12,min_length=3) -第二层:局部钩子 def validate_name(self, name): # 待校验的前端传入的name的数据 if name.startswith("sb"): # 不行,抛异常 raise ValidationError('不能以sb开头') return name -全局钩子--》前端多传的,这里不会有 def validate(self, attrs): print(attrs) # 多个字段同时校验 # 出版社名和地址不能一样---》出版社前3个字不能和地址前3个字一样 if attrs.get('name')[:3] == attrs.get('addr')[:3]: raise ValidationError('出版社名和地址不能一样') return attrs # 使用步骤 -写序列化类:写三层规则 -视图类中: ser = PublishSerializer(data=request.data) # 把待校验数据传入 if ser.is_valid(): # 做数据校验---》三层 print(ser.data) else: print(ser.errors) # 没有校验通过,打印错误信息
序列化类保存
使用步骤: 1 在序列化类中,必须重写 create,完成真正的保存 # 保存,必须重写create def create(self, validated_data): # validated_data 校验过后的数据---》多传的数据,在这没有 publish = Publish.objects.create(**validated_data) return publish # 不要忘了返回新增的对象---》后续会拿着这个对象做序列化 ser.data--->根据它做序列化的 2 在视图类中,数据校验通过后,调用ser.save() ser.save() # 使用序列化类保存--》会报错---》咱们没有指定保存到那个表--》必须重写create方法 # 修改功能,也要校验和保存 # 修改使用步骤 1 在序列化类中,必须重写 update,完成真正的修改 def update(self, instance, validated_data): # {name:上海出版社,add:上海地址} instance.name=validated_data.get('name') instance.addr=validated_data.get('addr') instance.save() # publish 对象的save---》保存到数据中 return instance 2 视图类中 ser = PublishSerializer(instance=publish, data=request.data) ser.save() # 虽然新增或修改都是调用save,但是内部做了判断
————修改图书接口 兼容 json 和urlencoded ......request.data
模拟一个装饰器 在视图类中 无论以什么方式都能利用 request.data 取出字典
APIVIew和 Request源码 的流程》》》
Book表的五个接口 限制 敏感词
(APIVIew+Response ....序列化)
标签:入门,self,request,day3,publish,time,序列化,data,drf From: https://www.cnblogs.com/wzh366/p/17916325.html