CBV源码分析+APIVIew源码分析

时间:2023-03-17 21:25:14

django的请求生命周期

{drf,resful,apiview,序列化组件,视图组件,认证组件,权限组件,频率组件,解析器,分页器,响应器,URL控制器,版本控制}

一、CBV源码分析
准备工作:
新建一个Django项目
写一个基于类的视图

CBV源码分析+APIVIew源码分析
继承,写get,post方法

from django.shortcuts import render, HttpResponse

from django.views import View
from django.http import JsonResponse # Create your views here. class Test(View): def dispatch(self, request, *args, **kwargs):
# codes
obj = super().dispatch(self, request, *args, **kwargs)
# codes
return obj def get(self, request, *args, **kwargs):
return HttpResponse('cbv_get') def post(self, request, *args, **kwargs):
return HttpResponse('cbv_post')

视图写好之后写路由
把views导过来,

类名.as_view()  直接执行

from django.conf.urls import url
from django.contrib import admin
from appp01 import views urlpatterns = [
url(r'^admin/', admin.site.urls),
# 在这里写的 views.Test.as_view(),实际上就是as_view()里面view的内存地址
url(r'^test/', views.Test.as_view()), ]

分析一下执行的流程:
  如果当初放的是函数内存地址不会去执行,请求来了经过路由做分发。但是如果放的是函数加括号直接就执行了(程序一点一运行立马这里就执行),执行Test.as_view的方法,

  Test里面没有as_view,所以执行的是父类View里面的as_view方法

CBV源码分析+APIVIew源码分析

打开父类的View找到as_view方法,类来调用就把类传入,定义了一个view函数,最后返回了这个view,所以在路由层写的实际上就是view(as_view()内部的)这个函数的内存地址。

当请求来了,来了执行的实际上就是view加括号执行。在执行前

CBV源码分析+APIVIew源码分析

这里面外层函数有个cls(这个cls就是Test这个类,产生了对象赋值给了self。),在内部函数有个对外部作用域的引用,所以这个view就是一个闭包函数。

把请求的request对象赋到了self.request里面

最后返回了一个self.dispatch函数,其实就是在执行这个函数。

这个dispatch函数就是生成的Test这个类的,这个Test类里面没有这个dispatch,所以执行的是View的dispatch。

所以请求来了就是执行dispatch方法

这里面取到请求的小写,判断在不在self.http_method_names中,如果是get请求在这个列表里面

通过getattr反射,self是Test对象,找到get方法,找不到返回错误提示(

self.http_method_not_allowed

handler=test对象中get方法的内存地址,

两层判断。

CBV源码分析+APIVIew源码分析

看self.http_method_names源码

CBV源码分析+APIVIew源码分析

self.http_method_not_allowed源码

CBV源码分析+APIVIew源码分析

源码执行的流程?
CBV源码分析
cbv和fbv
1 在views中写一个类,继承View,里面写get方法,post方法
2 在路由中配置: url(r'^test/', views.Test.as_view()),实际上第二个参数位置,放的还是一个函数内存地址
3 当请求来了,就会执行第二个参数(request,参数),本质上执行view()
4 view内部调用了dispatch()方法
5 dispatch分发方法,根据请求方式不同,执行的方法不同

二、resful规范  ----->软件开发的一种规范,好处是方便前台跟后台交互,后台跟后台交互可以直接访问
面向资源架构,面向资源编程。把网络上所有东西都当成资源。

10个规范
1.与后台做交互,通常采用https协议,这样安全

2.域名 有两种格式
https://api.baidu.com 这就说明这是百度提供的这个接口,这种存在跨域问题。
https://www.baidu.com.api/

3.版本 ,接口不能保证不改动
https://www.baidu.com.api/v1
https://www.baidu.com.api/v2

4.路径上所有的东西都是资源,均使用名词来表示。不用动词get,delet。。。
https://www.library.com.api/v1/books/ 拿所有书
https://www.library.com.api/v1/book/ 拿一本书

5.method(请求的方式)来表示增删查改,链接上看不出来只有在请求方式上能看出来增删查改。
https://www.library.com.api/v1/books/ get请求,获取所有书
https://www.library.com.api/v1/books/ post请求,新增一本书
https://www.library.com.api/v1/book/1 delete请求,删除一本id为1的书
https://www.library.com.api/v1/book/1 get请求,获取id为1的这本书
https://www.library.com.api/v1/book/1 put/patch请求,修改id为1的这本书

6.过滤条件
https://www.library.com.api/v1/books?limit=10 只拿前十本书
https://www.library.com.api/v1/books?price=20 只拿价格为20元的书

7.状态码
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
等价于:{'status':100,}

8.错误处理,返回错误信息,error当做key
{status:101,error:"不可以这样"}

9.返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档

10.返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
返回结果中提供链接(获取一本书)
{
id:1
name:xxx
price:12
publish:www.xxx.com.api/v1/publish/1

}

三、Django中写resful的接口
路由设计很重要。

urlpatterns = [
url(r'^users/', views.users),
url(r'^user/(?P<id>\d+)', views.user),
]

模拟数据库,列表嵌套字典

users_list = [{"id":1,"name":'zrg',"age":18},{"id":2,"name":'yj',"age":19},{"id":3,"name":'xyw',"age":18}]

def users(request):
# 返回字典里面写status,如果成功的话。 需要把users_list取到的数据赋给response
response = {'status': 100, 'erros': None}
# 如果method等于get请求的话获取所有用户,并且返回resful规范
if request.method == 'GET':
response['users'] = users_list
# 导入JsonResponse,字典里面套了列表所以safe=False
return JsonResponse(response, safe=False)
# 如果method等于post请求,新增
if request.method == 'POST':
# 新增的话,数据取出来添加到users_list里面去
name = request.POST.get('name')
age = request.POST.get('age')
users_list.append({'id': len(users_list) + 1, 'name': name, 'age': age})
# response['user']={'name':name,'age':age}
response['msg'] = '新增成功'
return JsonResponse(response)

# 单个的增删改
# 把参数传过来,通过分组来做(有名无名分组)

# 单个的增删改
# 把参数传过来,通过分组来做(有名无名分组)
def user(request, id):
response = {'status': 100, 'erros': None}
if request.method == 'GET':
id = int(id)
response['user'] = users_list[id]
return JsonResponse(response)

用postman提交不会自动补/,在浏览器会自动补/(中间件的原理添加)。

所以在postman这个软件里用就写全的路径!get请求可以重定向,post请求没有重定向这一说。

四、drf写resful的接口   是一个app
drf:Djangorestframework
1.安装

pip3 install Djangorestframework

在pycharm里也可以安装

2.简单使用

要使用先去APP里注册

CBV源码分析+APIVIew源码分析

基于drf写接口,写cbv,视图层# 基于drf写接口,写cbv
# 没有也可以写,有了更好写接口
# 导入rest_framework里面的views
from rest_framework.views import APIView
from rest_framework.response import Response # Response 也是继承了HttpResponse,这个Response就传一个字典就可以了,会自动序列化。 # 写类继承APIView,写get,post方法
# class DrfTest(views.APIView):
# 等同于
class DrfTest(APIView):
def get(self, request, *args, **kwargs):
response = {'status': 100, 'erros': None}
response['users'] = users_list
# 传列表传字典
# 用drf的Response,可以通过请求客户端来判断返回数据格式是什么样的,用浏览器返回的是页面。。。
return Response(response) # 返回JsonResponse也可以,有数据,没页面 def post(self, request, *args, **kwargs):
name = request.data.get('name')
pass
# 原生Django只能处理form-data 和 urlencode 编码,json处理不了,drf就能处理了,但是取值从大写POST里面取值。
    def post(self, request, *args, **kwargs):
# 传参
# data方法这里面是所有编码方式都能转成字典
name = request.data.get('name') #从data这个字典里拿到post提交的form-data 和 urlencode ,json格式的数据
print(name)
return HttpResponse('ok')

APIVIew源码分析
dispatch方法:
# 传入的request是原生的request对象
# 这个request已经不是原生的request了,但是它内部有个原生的request对象

CBV源码分析+APIVIew源码分析

CBV源码分析+APIVIew源码分析

CBV源码分析+APIVIew源码分析

CBV源码分析+APIVIew源码分析

request = self.initialize_request(request, *args, **kwargs)
self.initial(request, *args, **kwargs)#这里面有权限,认证,频率

Request源码的分析

as_view,dispatch,self.initialize_request,里面Request

request.data实际上是个方法,才转换数据(如果前端传的formdata,URLencode就从大写post返回,如果json从body体里拿出来转换回来。---->解析器)

也有request.method,触发attr,反射原来的request

request.POST,等价于request._request.POST

request.GET

Request源码分析
request.data其实是个方法,被包装成了属性
重写了__getattr__

print(request.GET)
# 就相当于:
print(request.query_params)   query_params----->查询的参数

五、drf之序列化组件

  序列化:把Python中的对象转成json格式字符串

  反序列化:把json格式转换成我樱桃红对象

序列化组件

Serializer

ModelSerialier

class MEta:

  model

  fields

  depth

  publish=

全局,局部钩子函数

反序列化:

  保存

  修改

全局跟局部钩子 看源码底往上走,自己-父类-父类的父类。。。。 self当前序列化的对象

----------------- ------------------- ------------------- -------------

第二步 使用 books_xlh = BookXlh(books_l,many=True)

books_xlh.data就是序列化完成的字典
from django.shortcuts import render

# Create your views here.
from rest_framework.response import Response from rest_framework.views import APIView from app01 import models from app01.Myxlh import BookXlh # # 写一个获取所有图书的接口
# class Books(APIView):
# def get(self,request,*args,**kwargs):
# # 所有图书拿出来,转成json格式
# books_l=models.Book.objects.all()
# # 用的话,要序列化谁,就写一个序列化类(新建文件夹或py文件),这个类继承一个东西,
# # 然后用的时候直接用这个类生成一个对象就可以了。
# # 比如序列化这个book,就单独为这个book写个序列化的类
#
# # 用BookXlh需要导入
# # 第一个参数需要序列化的queryset对象,第二个参数many=True(如果序列化多条必须)
# books_xlh = BookXlh(books_l,many=True)
# # isinstance=单个对象的时候,many=False 比如 books_l=models.Book.objects.all().first
# print(books_xlh.data) #是一个字典
# return Response(books_xlh.data) # 优化以上代码
# 写一个获取所有图书的接口
class Books(APIView): def get(self, request, *args, **kwargs):
# 字典应该是:
response = {'status': 100, 'msg': 'successful'}
# 所有图书拿出来,转成json格式
books_l = models.Book.objects.all()
# 用的话,要序列化谁,就写一个序列化类(新建文件夹或py文件),这个类继承一个东西,
# 然后用的时候直接用这个类生成一个对象就可以了。
# 比如序列化这个book,就单独为这个book写个序列化的类 # 用BookXlh需要导入
# 第一个参数需要序列化的queryset对象,第二个参数many=True(如果序列化多条必须)
books_xlh = BookXlh(books_l, many=True)
# isinstance=单个对象的时候,many=False 比如 books_l=models.Book.objects.all().first
print(books_xlh.data) # 是一个字典
response['books'] = books_xlh.data
return Response(response) # 获取一本书的
class Book(APIView):
def get(self, request, id):
response = {'status': 100, 'msg': '成功了'}
book = models.Book.objects.filter(pk=id).first()
book_xlh = BookXlh(book, many=False)
response['book'] = book_xlh.data
return Response(response)

第一步先写一个类(要序列化哪个表模型,就对应着哪个表模型写一个序列化的类):class BookXlh(serializers.Serializer):

# 单独写类
from rest_framework import serializers
# from rest_framework.serializers import Serializer
from rest_framework.request import Request class BookXlh(serializers.Serializer):
# 写序列化哪个字段
id=serializers.CharField()
title=serializers.CharField()
price=serializers.CharField() # 用BookXlh需要导入
以上如果不指定source,字段名字必须跟数据库名字对应起来。

写路由
"""dj_cbv2 URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^books/', views.Books.as_view()),
url(r'^book/(?P<id>\d+)', views.Book.as_view()),
]

source

一旦指定了source,字段名跟source的值一定不能重复。

# 单独写类
from rest_framework import serializers
# from rest_framework.serializers import Serializer
from rest_framework.request import Request class BookXlh(serializers.Serializer):
# 写序列化哪个字段
id=serializers.CharField()
# title=serializers.CharField()
# 尽量让名字不要跟数据库名字重合,通过source=可以改名。
name=serializers.CharField(source='title')
price=serializers.CharField() # 拿publish,拿到的是名字 publish_name = serializers.CharField(source='publish.name')
publish_id = serializers.CharField(source='publish.pk') # 把出版社所有东西都拿出来
publish_dic=serializers.SerializerMethodField() def get_publisn_dic(self,obj): #obj 就是当前book对象
return {'id':obj.publish.pk,'name':obj.publish.name} #这个字典赋给get_publisn_dic # 用BookXlh需要导入 # 注意:
#1、 变量名和source='xx',不能重合
#2、source还支持继续点
#3、source不仅支持字段还支持执行方法
#4、支持写方法,如下,方法一定要传一个参数,就是当前book对象
# # 把出版社所有东西都拿出来
# publish_dic = serializers.SerializerMethodField()
#
# def get_publisn_dic(self, obj): # obj 就是当前book对象
# return {'id': obj.publish.pk, 'name': obj.publish.name} # 这个字典赋给get_publisn_dic

序列化组件的源码 (source字段后放字段也行,放方法也可以)

publish_dic = serializers.SerializerMethodField() 
一定要有个方法跟他对应
def get_publisn_dic(self, obj):
这个方法的返回值就会赋给这个变量publish_dic
这个方法名字也是固定的就是get+字段名

获取作者

先写类并继承

class AuthorXlh(serializers.Serializer):
id=serializers.CharField()
name=serializers.CharField()
age=serializers.CharField()

在book类里,序列化得到。

    authors = serializers.SerializerMethodField()
def get_authors(self,obj):
# 所有作者requeryset对象
authors_list=obj.authors.all() author_xlh=AuthorXlh(instance = authors_list,many=True)
return author_xlh.data
# 列表推导式
# l1=['name':author.name,'id':author.pk for author in authors_list ]

继承ModelSerializer,这个是跟表模型绑定的序列化。

class BookXlh(serializers.ModelSerializer):

from app01 import models
class BookXlh(serializers.ModelSerializer):
# 固定写法
class Meta:
# 指定表模型
model= models.Book
# 要序列化所有字段
fields = '__all__'
# 只想序列化title 和 id 这两个字段
# fields = ['title','id']
# 如果想要publish字段显示出版社名字 # 不包含什么字段
# exclude=['title'] #不能和fields连用 不显示title字段 #联表的深度
depth=1 #深度是1 有关联的话往下查一层
# 缺点 全取出来,下几层的参数不可控制 # 全取出来之后,可以覆盖前面的值 ------>相当于重写某些字段
# 多写一个字段
publish = serializers.CharField(source='publish.name')
# 作者的详细信息
authors = serializers.SerializerMethodField()
def get_authors(self,obj):
# 拿到
authors_l=obj.authors.all()
# 序列化
author_xlh=AuthorXlh(instance=authors_l,many=True) return author_xlh.data

钩子函数

from app01 import models
class BookXlh(serializers.ModelSerializer):
# 固定写法
class Meta:
# 指定表模型
model= models.Book
# 要序列化所有字段
fields = '__all__'
# 只想序列化title 和 id 这两个字段
# fields = ['title','id']
# 如果想要publish字段显示出版社名字 # 不包含什么字段
# exclude=['title'] #不能和fields连用 不显示title字段 #联表的深度
# depth=1 #深度是1 有关联的话往下查一层
# 缺点 全取出来,下几层的参数不可控制 title = serializers.CharField(max_length=32, min_length=2, error_messages={'max_length': '太长了'}) # 局部跟全局钩子 # 这个参数是title条件过来的那个参数, def validate_title(self, value):
from rest_framework import exceptions
if value.startswith('sb'):
raise exceptions.ValidationError('不能以sb开头')
return value # 全取出来之后,可以覆盖前面的值 ------>相当于重写某些字段
# 多写一个字段
# publish = serializers.CharField(source='publish.name')
# # 作者的详细信息
# authors = serializers.SerializerMethodField()
# def get_authors(self,obj):
# # 拿到
# authors_l=obj.authors.all()
# # 序列化
# author_xlh=AuthorXlh(instance=authors_l,many=True)
#
# return author_xlh.data

增删查改的接口写法

   # 增加一本书,一般放在books里发请求上
# 新增怎么增?-----> 提交的字典,创建一个对象去保存(快速的方法,通过序列化组件保存----->必须继承自serializers.ModelSerializer),校验通过,默认不能为空
def post(self,request, *args, **kwargs):
response = {'status':100,'msg':'新增成功'}
book = request.data
book_xlh=BookXlh(data=book) #其实data=request.data # 什么数据都没写,默认不能为空 ,但是长度啊什么的没有限制
# 提交的子弹通过校验
if book_xlh.is_valid():
book_xlh.save()
response['book'] = book_xlh.data
else:
response['msg']=book_xlh.errors # return Response(book_xlh.data) return Response(response)

    # 删除,查出来直接delete
def delete(self,request,id):
response = {'status': 100, 'msg': '删除成功了'}
book = models.Book.objects.filter(pk=id).first().delete() return Response(response)

查就是获取

# 获取一本书的
class Book(APIView):
def get(self, request, id):
response = {'status': 100, 'msg': '成功了'}
book = models.Book.objects.filter(pk=id).first()
book_xlh = BookXlh(book, many=False)
response['book'] = book_xlh.data
return Response(response) # 修改书在book里面,put和putch请求都可以
def put(self,request,id):
response = {'status': 100, 'msg': '成功了'}
book = models.Book.objects.filter(pk=id).first()
book_xlh = BookXlh(data=request.data,instance=book) if book_xlh.is_valid():
book_xlh.save()
response['book'] = book_xlh.data
else:
response['msg']=book_xlh.errors return Response(response)

什么是跨域?

什么是幂等性?