drf APIView执行流程,Request对象源码分析,序列化器介绍与使用
昨日内容回顾
restful 规范
1接口使用https协议传输数据保证数据的安全性
2接口带api标识
3接口带版本信息
4接口地址即资源,尽量使用名字,或复数,特殊情况可以使用动词
5接口使用请求方式操作资源
6返回数据响应状态码:http响应状态码,自定义响应状态码
7返回数据应该改有错误信息{code:100,msg:'成功'}
8返回数据规范
查所有返回数组
查单个返回对象
新增返回新增对象
修改返回修改对象
删除返回空文档
9返回接口带链接
10接口带过滤添加(查询条件)
序列化反序列化
核心就是数据的格式转换
前后端分离:
序列化:查出的模型数据--json数据字符串--给前端 read
反序列化:前端传入json格式字符串--转换成python字典--模型对象--存入数据库 write
使用django原生编写5个接口
前后端分离,写的都是接口,接口基本上都是基于这5种的变形
查询所有 :Jsonresponse
新增:request.POST取出前端传来body的数据,只能取出form-data urlencode编码格式的数据 不能取出json格式的数据 需要先反序列化字典在使用
查单个:根据pk查询到单个对象,直接序列化对象返回给前端
修改单个:根据pk查到对象,put请求数据放到body中,request.POST取不到数据,只能自己手动取,然后转为字典对象
删除:pk直接删除
drf介绍
django的一个app组件,最新版为3.x支持django从3.x 到最新
框架,模板不一致可能会导致运行不起来
方便我们快速的在django框架上编写符合restful规范的接口
快速使用drf写5个接口
路由:自动生成路由
视图:
系列化类:serializer
CBV源码分析
路由中:path('api/v1/books/', views.BookView.as_view())--》匹配成功执行--》view.BookView.as_view()(request)--》由于当前类没有,找父类有,执行父类的as_view方法,返回闭包函数的view内存地址然后再加括号调用--》return sefl.dispatch(request,*args, **kwargs)-->这个self就是BookView产生的对象-->发现BookView没有dispatch方法--》View类中有
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
# self是BookView的对象,通过字符串get找方法或属性,找到了get方法
#handler就是BookView类的get方法
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# 执行 BookView的get(request)---->get请求就会执行BookView的get方法,post请求就会执行BookView的post方法
return handler(request, *args, **kwargs)
使用pycharm打开sqlite的问题
sqlite文件--》文件(xx.mp4,xx.txt)--->是一样的都是存储的二进制
我们选择什么软件打开,看到的东西是不一样的
解决:
干脆不用pycharm打开使用nvaicate打开
使用pycharm正常连接
今日内容概要
drf整体内容
APIView执行流程(难,了解)
Request对象原阿门分析(难,了解)
序列化器介绍和快速使用
反序列化
基于序列化器编写的5个接口(重要)
反序列化的校验
今日内容详细
drf整体内容
1 入门规范
-web开发模式
-api接口
-接口测试工具
-restful规范
-序列化反序列化
-drf快速使用
-APIView,Request类
-drf把django的好多东西都重写了
2 序列化组件(重要)
-基本序列化类
-模型序列化类
-如何序列化
-如何反序列化
-反序列化的数据校验功能
3 请求与相应
drf的request已经不是原来的django的request
drf的reponse已经不是原来的django的reponse
指定可以接收请求的编码方式
指定响应格式
4 视图组件(重点)
两个视图基类
5个视图扩展类
9个视图子类
视图集
5 路由
自动生成路由
6 认证(登录认证),权限,频率(重点)
7 过滤,排序,分页(次重点)
8 自动生成接口文档
9 JWT登录认证的,区别于cookie和session,主流
10 后台管理美化(django原生的很丑,simpleui二次开发)
11 基于角色的访问控制,权限的控制
APIView执行流程(难,了解)
基于APIView+JsonResponse编写接口
# 原来基于django原生的View编写接口
# drf提供给咱们的一个类,以后使用drf写视图类,都是继承这个类及其子类,APIView本身就是继承了Django原生的View
class BookView(APIView):
def get(self, request):
books = Book.objects.all()
book_list = []
for book in books:
book_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
return JsonResponse(book_list, safe=False)
基于APiView+Response写接口
class BookView(APIView):
def get(self, request):
books = Book.objects.all()
book_list = []
for book in books:
book_list.append({'name': book.name, 'price': book.price, 'publish': book.publish})
return Response(book_list) # 无论是列表还是字典都可以序列化
APIView的执行流程
# 路由中写的: path('books/', views.BookView.as_view()),---》请求来了,执行views.BookView.as_view()()----->现在的as_view是APIView的as_view
# APIView的as_view方法:view还是原来的view,但是以后再也没有csrf认证了
@classmethod
def as_view(cls, **initkwargs):
# 调用父类的as_view,父类是django原生的View
# 把djagno原生View的as_view方法中的闭包函数view拿出来了
view = super().as_view(**initkwargs)
# csrf_exempt 排除所有csrf的认证
# 相当于在所有的方法上面加了这个装饰器
return csrf_exempt(view)
# 路由匹配成功,执行 csrf_exempt(view)(requets)--->View的as_view中的闭包函数view---》self.dispatch---->self是视图类的对象---》BookiView---》APIView的dispatch,找到了
def dispatch(self, request, *args, **kwargs):
# request是django原生的request,老的request
# 把老的request包装成了新的request,这个是drf提供的Request类的对象
request = self.initialize_request(request, *args, **kwargs)
# 到此以后,这个request就是新的了,老的request在哪?
# request._request 这是老的
# 把新的request放到了self对象【BookView的对象】
self.request = request
try:
# 执行了三大认证【认证,频率,权限】,使用新的request,不读
self.initial(request, *args, **kwargs)
# 跟之前一毛一样
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
# 把新的request传入了,视图类的方法中get的request也是新的
response = handler(request, *args, **kwargs)
except Exception as exc:
# 在执行3大认证和视图类中方法的过程中,如果出了异常,都能捕获到---》全局异常捕获
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
总结:APIView的执行流程
-
去除了所有的csrf认证
-
包装了django原生的request成了新的request,以后再视图类中用的request是新的request Request类的对象,不是原生的了
原生的request再request._request
-
在执行视图类之前,执行了3大认证
-
如果再3大认证或视图函数方法执行过程出错,会有异常捕获—》全局异常捕获
-
以后视图类中的request都是新的了
补充:装饰器的基本原理
def auth(func)
def inner(*args,**kwargs)
res = func(*args,**kwargs)
return res
return inner
@auth #本质是 add = auth(add)
def add():
pass
以后使用add(),其实就是在使用 auth(add)的返回的结果
Request对象源码分析(难,了解)
新的Request—》区别于老的
老的:django.core.handlers.wsgi.WSGIRequest
新的:from rest_framework.request import Request
新的 request._request 是老的request
Request源码
_getattr_
在视图类的方法中,执行request.method,新的request是没有method的,就触发了新的request的__getattr__方法的执行
def __getattr__(self, attr):
try:
# 从老的request中反射出 要取得属性
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
request.data—>这个方法,包装成了数据属性
以后无论post,put….放在body中提交的数据,都从request.data中取,取出来就是字典
无论哪种编码格式都从这里面取
@property
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
requset.query_parms—》这个方法,包装成了数据属性
地址栏中携带的参数,以后从这里面取
也就是?xxx=xxx&yyy=yyy这中类型的
@property
def query_params(self):
"""
More semantically correct name for request.GET.
"""
return self._request.GET
request.FILES—>这是个方法,包装成了数据属性
前端提交过来的文件,从这里面取
@property
def FILES(self):
# Leave this one alone for backwards compat with Django's request.FILES
# Different from the other two cases, which are not valid property
# names on the WSGIRequest class.
if not _hasattr(self, '_files'):
self._load_data_and_files()
return self._files
Request类总结
- 新的request用去来,跟之前一模一样,因为新的取不到,会取老的_getattr_
- request.data 无论什么编码,什么请求方式,只要是body中的数据,就从这里面取,取出的是字典类型
- request.query_parms就是原来的request._request.GET
- 上传的文件从request.FILES
python中有很多的魔法方法,在类中,某种情况下会触发,会自动执行
_str_:打印对象会调用
_init_:类()会调用
_call_:对象()会调用
_new_:
_getattr_:对象.属性,如果属性不存在,会触发它的执行
序列化器介绍和快速使用
因为在写接口是需要序列化,需要反序列化,而反序列化的过程中要做数据校验—》drf直接提供了固定的写法,只要按照固定写法使用,就能完成上面的三个需求
提供了两个类 Serializer ModelSerializer
以后咱们只需要写自己的类,继承drf提供的序列化类,使用其中的某些方法,就能完成上面的操作
序列化类基本使用,序列化多条
serializer.py–>BookSerializer类
from django.db.models.query_utils import DeferredAttribute
from rest_framework import serializers
from .models import Book
from collections import OrderedDict
class BookSerializer(serializers.Serializer):
#自己一个一个写
# name = serializers.CharField()
# price = serializers.CharField()
# publish = serializers.CharField()
# pass
def create(self, validated_data):
books = Book.objects.create(**validated_data)
return books
def update(self, instance, validated_data):
for key in validated_data:
setattr(instance, key, validated_data.get(key))
instance.save()
return validated_data
#序列化除id的其他字段
#自动写
fields = [field for field, obj in Book.__dict__.items() if isinstance(obj, DeferredAttribute)]
fields.remove('id')
# print(fields)
fields_list = []
for name in fields:
fields_list.append((name, serializers.CharField()))
setattr(BookSerializer, '_declared_fields', OrderedDict(fields_list))
views.py–>BookView类
class BookView(APIView):
def get(self, request):
"""查所有"""
# 只是为了验证之前讲过的
print(request.method)
print(request._request)
print(type(self.request))
books = Book.objects.all()
# 使用序列化类来完成---》得有个序列化类
# instance要序列化的数据books queryset对象
# many=True 只要是queryset对象要传 many=True 如果是单个对象就不用传默认为False
ser = BookSerializer(instance=books, many=True)
return Response(ser.data) # 无论是列表还是字典都可以序列化
序列化单条
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 序列化某些字段,这里写要序列化的字典
name = serializers.CharField() # serializers下大致跟models下的类是对应的
# price = serializers.CharField()
publish = serializers.CharField()
views.py–>BookViewBody
class BookViewBody(APIView):
def get(self, request, *args, **kwargs):
"""查询单个信息"""
book = Book.objects.filter(pk=kwargs.get('pk')).first()
ser = BookSerializer(instance=book)
return Response(ser.data)
urls.py—>添加新的路由
urlpatterns = [
path('books/<int:pk>/', views.BookViewBody.as_view()),
]
反序列化
反序列化的新增
序列化类
class BookSerializer(serializers.Serializer):
#自己一个一个写
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
def create(self, validated_data):
# 保存的逻辑
# validated_data 校验过后的数据 {name,price,publish}
books = Book.objects.create(**validated_data)
# 一定要返回新增的对象
return books
veiws.py
class BookView(APIView):
def post(self, request):
# requset.data # 前端提交的要保存的数据----》校验数据---》存
ser = BookSerializer(data=request.data) # 把前端传入的要保存的数据,给data参数
# 校验数据
if ser.is_valid():
# 保存---->需要自己写,要在序列化类BookSerializer中写----》create方法
ser.save() # 调用ser.save,自动触发咱们写的create,保存起来
return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
反序列化的修改
序列化类
class BookSerializer(serializers.Serializer):
# 序列化某些字段,这里写要序列化的字典
name = serializers.CharField() # serializers下大致跟models下的类是对应的
price = serializers.CharField()
publish = serializers.CharField()
def create(self, validated_data):
# 保存的逻辑
# validated_data 校验过后的数据 {name,price,publish}
# 保存到数据库
book = Book.objects.create(**validated_data)
# 一定不要返回新增的对象
return book
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() # orm的单个对象,修改了单个对象的属性,只要调用对象.save,就能把修改保存到数据库
return instance # 不要忘了把修改后的对象,返回
#也可以使用下main这种
def update(self, instance, validated_data):
for key in validated_data:
setattr(instance, key, validated_data.get(key))
instance.save()
return validated_data
views.py
class BookDetailView(APIView):
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
# 反序列化保存 ---借助于序列化类
ser = BookSerializer(data=request.data, instance=book)
if ser.is_valid():
ser.save() # 由于没有重写update,所以这报错
return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
删除单条
views.py
def delete(self, request, pk):
"""删除单条"""
Book.objects.filter(pk=pk).delete()
return Response()
基于序列化器编写的5个接口(重要)
反序列化的校验
序列化与反序列化,数据校验功能—》类比form组件
局部钩子
全局钩子
from django.db.models.query_utils import DeferredAttribute
from rest_framework import serializers
from .models import Book
from collections import OrderedDict
from rest_framework.exceptions import ValidationError
class BookSerializer(serializers.Serializer):
#自己一个一个写
# name = serializers.CharField()
# price = serializers.CharField()
# publish = serializers.CharField()
# pass
def create(self, validated_data):
books = Book.objects.create(**validated_data)
return books
def update(self, instance, validated_data):
for key in validated_data:
setattr(instance, key, validated_data.get(key))
instance.save()
return validated_data
def validate_name(self,name):
"""局部钩子"""
if name.startswith('sb'):
raise ValidationError('不能以namesb开头')
return name
def validate(self, attrs):
#全局钩子
if attrs.get('name')==attrs.get('publish'):
raise ValidationError('书名与出版社名不能一样')
return attrs
作业
APIView和Request源码分析,自己分析一遍
基于序列化器编写5个接口 Publish
使用局部钩子和全局钩子校验 5个接口
view.py
from django.shortcuts import render
# Create your views here.
from rest_framework.views import APIView
from .models import Book
from .serializer import BookSerializer
from rest_framework.response import Response
class BookView(APIView):
def get(self, request):
books = Book.objects.all()
ser = BookSerializer(instance=books, many=True)
return Response(ser.data)
def post(self, request):
ser = BookSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
return Response(ser.errors)
class BookDetailView(APIView):
def get(self, request, publish):
book = Book.objects.filter(publish=publish).first()
ser = BookSerializer(instance=book)
return Response(ser.data)
def put(self, request, publish):
book = Book.objects.filter(publish=publish).first()
ser = BookSerializer(instance=book, data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
return Response(ser.errors)
def delete(self, request, publish):
Book.objects.filter(publish=publish).delete()
return Response()
urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/books/',views.BookView.as_view()),
path('api/v1/books/<str:publish>/',views.BookDetailView.as_view()),
]
serializer.py
from rest_framework import serializers
from .models import Book
from rest_framework.exceptions import ValidationError
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
def create(self, validated_data):
book = Book.objects.create(**validated_data)
return book
def update(self, instance, validated_data):
for k, v in validated_data.items():
setattr(instance, k, v)
instance.save()
return validated_data
def validate_name(self, name):
if len(name) < 5:
raise ValidationError('名字长度小于5')
return name
def validate(self, attrs):
if not attrs:
raise ValidationError('不能传空数据')
return attrs
models.py
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.CharField(max_length=32)
原生的request 没有data 属性 实现一个原生的 request.data 拿出无论什么编码格式提交的数
from django.views import View
import json
def dictdata(func):
def inner(self, request, *args, **kwargs):
if request.POST: # 判断有没有值,有值说明是form-data urlencode这两种下面都可以用
# data_dict = {}
# for k, v in request.POST.items():
# data_dict[k] = v # 过滤列表
request.data = {k: v for k, v in request.POST.items()}
else:
request.data = json.loads(request.body) # 这种是json格式携带参数的,因为django不会帮我们解析
res = func(self, request, *args, **kwargs)
return res
return inner
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt, csrf_protect
class BookRawView(View):
@method_decorator(csrf_exempt) # 取消csrf校验只能加载dispatch上
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
@dictdata
def post(self, request):
ser = BookSerializer(data=request.data)
if ser.is_valid():
Book.objects.create(**ser.data)
return JsonResponse(request.data)
return JsonResponse(ser.errors)