day02 CBV和APIView源码剖析


day02 CBV和APIView源码剖析

今日内容概要

  • CBV的源码分析
  • drf之APIView的执行流程分析
  • drf的Request对象分析
  • 序列化类Sealizer的使用

源码剖析遵循的规律

# 要想看源码必须遵循一个规律
    1.先在自己定义中的类找有没有相应的函数
    2.如果自己类中没有,就去自己的父类中寻找

CBV的源码分析

前提准备

# urls.py
from django.urls import path,re_path  # 要想使用正则,需要改成re_path
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),  # path是精准匹配
    path('index/', views.indexViews.as_view())
]

# views.py
from django.shortcuts import render,HttpResponse
from django.views import View

class indexViews(View):  # CBV中必须定义一个类,而且要继承View
    def get(self,request):  # 这里必须是dispatch中8个方法,其中request是从dispatch传过来的
        return HttpResponse('ok')

CBV的源码分析

# 源码剖析
1.先在入口中进入源码
	path('index/', views.indexViews.as_view())  # as_view()入口
    
2.定义了一个View类,里面绑定一个as_view的函数,并且在其中调用dispatch方法用来处理请求
    class View:
        def as_view(cls, **initkwargs):  # 其中的cls,类名当成参数传递过来
            def view(request, *args, **kwargs):  # 在其中定义了一个view方法
                # self就是indexView类的对象
                self = cls(**initkwargs)
                return self.dispatch(request, *args, **kwargs)  # 在其中调用dispatch方法
            return view
        
3.使用dispatch方法处理请求
    def dispatch(self, request, *args, **kwargs):
        # 判断发来的请求是否在解释器定义的8中请求中
        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
        # 返回的是8种请求中的内存地址,加括号传递参数
        return handler(request, *args, **kwargs)

drf之APIView的执行流程分析

前提准备

# makemigrations app01  后面可以跟应用名称,意思只迁移该应用

# 前提准备

# urls.py
    from django.urls import path,re_path  # 要想使用正则,需要改成path
    from app01 import views
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('books/', views.BookViews.as_view())   # 入口在这
    ]

# views.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framewor	k.request import Request

    class BookViews(APIView): 
        def get(self,request):
            book_list = models.Book.objects.all()
            l = []
            for book in book_list:
                l.append({'title':book.title,'price':book.price})
            return Response(l)
    '''
    注:
        如果想要使用Response,必须先继承APIView才能使用
        因为drf是第三方app,所有要去settions.py中注册
    '''

drf之APIView的执行流程分析

# 源码剖析
1.先在入口中进入源码
     path('books/', views.BookViews.as_view())
    '''先去自己定义的类中找as_view方法,没有再去父类中寻找'''
    
2.去APIView父类找as_view方法
    class APIView(View):  # APIView继承了View类
        @classmethod
        def as_view(cls, **initkwargs):  # 在APIView中找到了as_view方法
            # 调用了View中的as_view赋值给view
            view = super().as_view(**initkwargs) # view现在是:View中as_view方法中的view函数内存地址
            view.cls = cls
            view.initkwargs = initkwargs
            return csrf_exempt(view)  # 去掉crsf验证,返回view内存地址
        
3.如果get请求发过来了,下一步怎么走
	# 走View类的as_view方法中的小view函数
	path('books/', views.BookViews.as_view())
     def view(request, *args, **kwargs):
            # self 就是IndexView类的对象
            self = cls(**initkwargs) # IndexView()
            # 在这里就不要点了,去继承APIView类中找到dispatch方法
            return self.dispatch(request, *args, **kwargs)  # 调用dispatch方法
        
# APIView的源码分析:
	'''根据规则:先在自己类中寻找dispatch方法,再去继承类中找,然后在去父类找'''
    
    # 在继承APIView类中找到dispatch方法
    def dispatch(self, request, *args, **kwargs):
        # 封装新的request方法,self中的是django中request
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request  # 封装后的drf的request

        try:
            # 这里面做了三大认证:认证,权限,频率
            self.initial(request, *args, **kwargs)  # 认证入口
            
            # 和View中的dispatch中一样,处理发送的请求
            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
                
		   # 把8种请求中的内存地址赋值给response
            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
    
 # 重点:
	APIView做了三件事:
        1. 把Django的request封装了新的request,drf的request
        	以后你在视图函数中,在使用request,就是新的request了。
            
        2. 进行了三大认证:
            self.perform_authentication(request)  # 认证
            self.check_permissions(request)       # 权限
            self.check_throttles(request)         # 频率
            
        3. 做了全局的异常处理
    
    # 以后,只要继承了APIView, 在视图函数中使用的request都是新的request。
    在执行视图函数之前,进行了三大认证。

drf的request对象分析

# 在这里封装了新的request
from rest_framework.request import Request  # 导入Request类
class APIView(View):
    def dispatch(self, request, *args, **kwargs):
        request = self.initialize_request(request, *args, **kwargs)  # 进入initialize_request
        
# 这里进入Request中
    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(  # 在这里进入Request中
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    
    
# 这里进入Request中
class Request:
    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        assert isinstance(request, HttpRequest), (
            .format(request.__class__.__module__, request.__class__.__name__)
        )
		# 重点再下面一句话
        self._request = request  # 把django中request赋值给_request
        
# 为什么drf中request和Django中的request使用起来一模一样?
        print(request.method)  # 用法和封装前一模一样

    是因为再Request类中定义了__getattr__方法
    '''
    __getattr__:对象.属性,当属性不存在,会自动触发__getattr__方法
    __setattr__:给不存在属性赋值的时候执行,会自动触发__setattr__方法
    '''
    # 是因为在Request类中定义了__getattr__方法
     def __getattr__(self, attr):
            try:
                return getattr(self._request, attr)
            except AttributeError:
                return self.__getattribute__(attr)

      # 新的request有什么特性?
    	'''
    	在Response类中定义了data函数
        多了一个data属性,针对post请求的三种编码格式都可以从data属性中取到
        使用data的前提:是必须继承APIView
    	'''
        @property
        def data(self):
            if not _hasattr(self, '_full_data'):
                self._load_data_and_files()
                return self._full_data

需要记住的几个方法

# 记住的几个属性和方法
	- _request:原来的django 的request
	-data:post请求提交的数据---》放在请求体中--》编码格式:urlencoded,form-data,json
        -urlencoded:     name=lqz&age=19   --->到了django中,从request.POST中取出来
        -json格式: {"name":"lqz","age":19}--->到了django,从request.POST中取不出来,需要自己做

        -最终:drf帮咱们干好了,以后无论urlencoded,form-data,json哪种格式,数据都在data这个字典中
    
    -query_params:   127.0.0.1/books/?name=红楼梦&price=19
    	-get请求提交的数据,放在这个字典中
        
    -重写了 __getattr__      # python的面向对象类中如果以 __开头__结尾的方法叫  魔法方法--》它不需要调用,是在某种情况下触发的
    -对象.属性  如果属性不存在,触发它
    -request.method--->request._request.method

序列化器-Serializer

作用:
    1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
    2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
    3. 反序列化,完成数据校验功能

序列化类Seaializer的使用

Django2默认支持以下5个转化器:

str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式  
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)

# 序列化类Seaializer的使用
    1.在应用下建议任意名的py文件,并在文件中导入
    2.在views中导入

urls.py

from django.contrib import admin
from django.urls import path,re_path  # 要想使用正则,需要改成path
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    # path('index/', views.indexViews.as_view()),
    path('books/', views.BookViews.as_view()),
    # 使用django2的转换器
    path('books//', views.BookDataViews.as_view())
]

views.py

from django.shortcuts import render, HttpResponse
from app01 import models

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from app01.serializer import BookSerializer  # 导入序列化类

class BookViews(APIView):
    # 查询所有图书的接口
    def get(self, request):
        book_list = models.Book.objects.all()
        ser = BookSerializer(instance=book_list, many=True)
        '''
        instance=book_list
        instance;要序列的数据
        many:如果序列化的数据是多条数据,就必须加这个参数
        '''
        return Response(ser.data)  # 数据在data里面

class BookDataViews(APIView):
    # 查询1条图书的接口
    def get(self, request,pk):
        book_obj = models.Book.objects.all().filter(pk=pk).first()
        ser = BookSerializer(instance=book_obj)
        return Response(ser.data)  # 数据在data里面

自定义序列化类

from rest_framework import serializers  # 必须导入这个模块
class BookSerializer(serializers.Serializer):
    # 写序列化的字段
    title = serializers.CharField()
    price = serializers.IntegerField()
drf