当前位置: 首页 > 工具软件 > ApiView > 使用案例 >

DRF之APIView源码分析

沈宇定
2023-12-01
url.py
	path('books/', BookView.as_view())
view.py
	class BookView(APIView):
        pass

1.我们看自己是否有as_view()方法,没有,那我们就来到继承的APIView类中
view = super().as_view(**initkwargs)  # 这是调用了父类View的as_View方法

return csrf_exempt(view)  # 只要是继承了APIView类的视图函数都失去csrf的保护

在View中的dispatch方法的时候,我们需要先看APIView类中有没有该方法,来到APIView中的dispatch方法:

	request = self.initialize_request(request, *args, **kwargs)  # 创建了一个新的request,后面使用的不再是Django中的request方法了
    self.request = request  # 更换掉request方法
    
    try:
        self.initial(request, *args, **kwargs)

        # Get the appropriate handler method(获取到适当的处理程序方法)
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(),  # 这里判断是否存在于7大方法中,eg:get
                              self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs)  # 返回request=get(request,*args,**kwargs)

   except Exception as exc:
           response = self.handle_exception(exc)

   self.response = self.finalize_response(request, response, *args, **kwargs)
   return self.response
    
     
APIView类
    1.包装新的request
    2.执行了认证,权限,频率
    3.处理了全局异常
    4.包装了request对象
drf是一个第三方的app,只能在django中使用
安装好drf之后,需要导入APIView视图类,后期使用drf写视图类,都是继承了APIView视图类或其子类


当我们使用request.data的时候,三种数据格式的结果方式是

1.urldecoded和form_data的结果是QueryDict形式
2.Json的结果是字典


使用View和JsonResponse写
继承的是View


当我们使用APIView+drf的Response写的时候,我们不要忘记注册rest_framework这个app
继承APIView视图类,可以使用request.data
原生的request.POST中,只有urlencoded和form-data格式提交的数据可以获取到,json格式提交的数据在body中,需要我们自己处理;但是在drf中,request中有一个data,data中可以取到任意的编码提交的数据


request._request.GET -最初始的request。  # 相当于是request.GET
request._request -新request
那么为什么新的request可以与老request执行结果相同。
因为触发了__getattr__魔法方法,何为魔法方法?不需要主动调用,某种状态下会触发。
__getattr__ 对象.属性 会触发并执行

def __getattr__(self, attr):
    try:
        return getattr(self._request, attr)
    except AttributeError:
        return self.__getattribute__(attr)
Request类源码分析:
    视图类中使用的request对象,已经变成了drf中提供的request类对象了
    1.原生的django的request是这个类的对象:django.core.handlers.wsgi.WSGIRequest
    2.drf的request是这个类的对象:rest_framework.request.Request
    
    虽然request已经不是以前的request了,但是用起来还是跟以前一样,只是加了几个特殊属性
    eg:
    print(request.method)  # get
    print(request.post)  # 路由/books/
    print(request.GET)  # 原来的get请求提交的参数
    print(request.POST)  # 原来的post请求提交的参数
    
    
    在源码中data其实是一个方法,只是被property装饰了,将方法变为了属性
    
    我们要获取get方法携带回来的数据的时候,我们使用query_params  等同于request._request.GET 或 request.GET
    
    文件数据还是从request.FILES中获取
view.py

from .serializers import BookSerializers

# 获取全部的数据
class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        ser_result = BookSerializers(instance=book_list, many=True)  # 根据需求序列化数据
        return Response(ser_result.data)  # 返回响应出序列化后的数据

    # 新增数据
    def post(self, request):
        data = request.data
        ser_result = BookSerializers(data=data)
        if ser_result.is_valid():  # 校验数据
            ser_result.save()  # 保存数据,这里我们需要重写create方法
            return Response(ser_result.data)
        else:
            return Response(ser_result.errors)


# 获取到单个数据
class BookDetailView(APIView):
    def get(self, request, pk):
        book = Book.objects.all().filter(pk=pk).first()
        ser_result = BookSerializers(instance=book)  # 单个数据many不用写
        return Response(ser_result.data)

    def put(self, request, pk):
        book = Book.objects.all().filter(pk=pk).first()
        ser_result = BookSerializers(instance=book, data=request.data)  # request.data修改到book
        if ser_result.is_valid():
            ser_result.save()  # 保存数据,但是我们也还要重写update方法
            return Response(ser_result.data)
        else:
            return Response(ser_result.errors)

    def delete(self, request, pk):
        Book.objects.all().filter(pk=pk).delete()
        return Response()
    
这里我们需要设置一个py文件,根据自己的需求设置序列化数据,还有数据的增加和修改需要重写create和update两个方法
serializers.py

from rest_framework import serializers
from .models import Book


# 要序列化的数据
class BookSerializers(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()

    def create(self, validated_data):  # validated_data是过滤之后的数据
        data = Book.objects.create(**validated_data)
        return data

    def update(self, instance, validated_data):  # instance是数据实列,validated_data是一个字典
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish = validated_data.get('publish')
        instance.save()
        return instance

drf中request有一个data方法,但是在源码中data其实是一个方法,只是被property装饰了,将方法变为了属性

1.不携带任何数据的时候,request.data返回的是空字典
2.urlencoded数据,request.POST与request.data都能接收到数据
3.Json格式数据,只能request.data接收,数据是先放在body中,然后通过loads反序列化,然后放在data中

request.data 包含了所有解析的内容,包括文件与非文件的输入

使用drf的时候,继承APIView视图类之后,drf提供了很多的view,它们都是来自于APIView类中

会包装出一个新的request,在视图中使用的时候,跟原来的情况差不多

但是需要注意两点:
	1.取post数据的时候,不要使用request.POST取,而是使用request.data取值
    2.取get提交的数据的时候,不要从request.GET中取,而是要从query_params中取
    
    
如果我们强烈的想使用原生django中的request,那我们应使用request._request


当执行form-data的时候,想打印request.data的时候,会报错,因为该格式可以携带文件数据,可能文件数据过大,防止数据过大破坏,所以就不会执行


当视图类继承的是View视图类的时候,使用put方法的时候,用request.POST取不到值


.content_type:
    request.content_type 返回HTTP请求正文的媒体类型的字符串对象,如果未提供媒体类型,则返回空字符串。也就是正文的格式。
    在drf中是可以用来访问请求的内容类型

APIView源码分析

路由层
path('books/', views.BookView.as_view())

视图层
class BookView(APIView):
	def get(self, request):
		book_list = Book.objects.all()
		serial = BookSerializer(instance=book_list, many=True)
		return Response(serial.data)
		
1.在路由层中BookView.as_view()先从BookView中查找as_view方法 找不到

2.再从父类APIView中查找as_view方法 在APIView中发现有as_view方法 加括号执行

3.在as_view中关键一步调用了父类原生View中的as_view方法view = super().as_view(**initkwargs)
  最后将view套上装饰器csrf_exempt(view)后的函数地址返回到路由后面path('books/', csrf_exempt(view)),
  这步操作可以免去自定义类中视图方法post,put...的csrf验证
  最后当路由匹配成功后会加括号执行csrf_exempt(view)(request, *args, **kwargs),进而执行下面的步骤

4.而在view方法中会调用dispatch方法 这时依然是按照从对象到类,再到父类 再到父类的父类的查找顺序
  这时会优先查找到并执行父类APIView中的dispatch方法 
  在这个dispatch方法中干了三件事
  (1.认证、频率、权限 2.进一步扩写request对象的方法属性 3.使用反射方法执行自定义视图类中对应的方法)

  1.调用APIView对象的方法request = self.initialize_request(request, *args, **kwargs)
	将原生django的request对象转化为DRF的request对象 让request对象保留原有的所有方法
	同时又新增了request.data属性(@property 本质上是一个方法) 这个方法将body中提交的数据反序列化之后都封装到了request.data中,
    也就是说无论是urlencoded格式、json格式还是form-data格式的数据,都封装进了request.data方法(属性)中
	但同时request.POST和request.FILES依然可以获取到对应的数据 需要注意的是这时保留在body中的
	json格式数据就不能通过request.body获取了 封装过后的request已禁止了此方法
    此外get请求后面?携带的数据依然通过query_params或request.GET获取
  
  2.在dispatch方法中用异常捕获执行了self.initial(request, *args, **kwargs)认证、频率、权限这步操作
    若在执行认证 频率 权限操作时报错了 就会返回其他数据 不会正常执行自定义视图类BookView中的get、post
	delete...等方法
	
  3.在dispatch中用异常捕获执行了反射方法 从自定义类中获取request.method对应的方法 若有就加括号执行此方法,并返回相应的结果
    若获取不到,则执行另外的代码 最终报错没有定义此方法


path('books/', BookView.as_view())

class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        ser = BookSerializer(instance=book_list, many=True)
        return Response(ser.data)
        
源码:
class APIView(View):
	@classmethod
	def as_view(cls, **initkwargs)
		view = super().as_view(**initkwargs)
		view.cls = cls
		return csrf_exempt(view)
		
	def dispatch(request, *args, **kwargs):
		reqeust = self.initialize_request  # 将request转化为drf的request
		self.reqeust = reqeust  # 替换原生request
		# 利用异常捕获 若执行认证、频率、权限、获取对象的方法报错 则会返回其他异常信息
		try:
            self.initial(request, *args, **kwargs)  # 频率 认证 权限

            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),  # 利用反射获取自定义对象中的get、post...方法
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs)  # 将反射获取到的方法执行结果赋值给response 最后返回response

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
		
class View:
	def as_vew(cls, **initkwargs):
		def view(request, *args, **kwargs):
			self = cls(**initkwargs)  # 这里的cls就是自定义类BookView,是这个类直接来调用的as_view方法(绑定给类的方法)
			return self.dispatch(reqeust, *args, **kwargs)
		return view
		
当路由匹配成功后加括号执行csrf_exempt(view)(request, *args, **kwargs)
最后执行APIView的dispatch方法
 类似资料: