django的请求生命周期
{drf,resful,apiview,序列化组件,视图组件,认证组件,权限组件,频率组件,解析器,分页器,响应器,URL控制器,版本控制}
一、CBV源码分析
准备工作:
新建一个Django项目
写一个基于类的视图
继承,写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方法
打开父类的View找到as_view方法,类来调用就把类传入,定义了一个view函数,最后返回了这个view,所以在路由层写的实际上就是view(as_view()内部的)这个函数的内存地址。
当请求来了,来了执行的实际上就是view加括号执行。在执行前
这里面外层函数有个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方法的内存地址,
两层判断。
看self.http_method_names源码
self.http_method_not_allowed源码
源码执行的流程?
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里注册
基于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对象
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)
什么是跨域?
什么是幂等性?