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()