APIView的使用、源码分析、Request类源码分析以及序列化组件介绍、基本使用、反序列化
APIView的基本使用
APIView是drf中views.py模块的一个类,它继承了django的View类,只能在django上使用
安装了drf后,我们再写类视图的时候,可以使用drf的APIView:继承APIView以及子类
class Books(APIView):
def get(self,request):
return HttpResponse('')
使用View+JsonResponse写一个接口
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from app01 import models
class Books(View):
def get(self,request):
res=models.Book.objects.all()
book_list=[]
for i in res:
book_list.append({'title':i.title,'price':i.price,'publish':i.publish})
return JsonResponse(book_list,safe=False,json_dumps_params={'ensure_ascii':False})
"""
注意:safe=False是非字典类型需要添加的参数,而json_dumps_params={'ensure_ascii':False})是JsonResponse继承于json,json_dumps_params的实参会打散作为json.dumps方法的关键字参数。
"""
使用APIView+drf的Response写一个接口
当我们使用drf的时候,一定不能忘记在settings.py中注册rest_framework
这个app
from rest_framework.response import Response
from rest_framework.views import APIView
from app01 import models
class Books(APIView):
def get(self,request):
res=models.Book.objects.all()
book_list=[]
for i in res:
book_list.append({'title':i.title,'price':i.price,'publish':i.publish})
return Response(book_list)
APIView源码分析
当我们的视图类继承APIView后,执行流程就发生了变化,这个变化就是整个drf的执行流程
源码分析入口还是和之前的cbv一样,从路由层视图类的as_view开始,只不过这个as_view是APIView了,因为继承了APIView。
由图可以看出:APIView的as_view方法会先调用父类的as_view,父类as_view里面有个闭包函数view,并返回了view,然后通过csrf_exempt
取消了csrf的校验并返回。
路由匹配成功之后会将类视图函数加括号执行,等于就是执行了view
函数,这个是父类View中的view,父类中然后返回视图类中对象中的dispatch方法,根据对象方法的查找顺序,会从自身开始查找,APIView中有,就会执行APIView的dispatch方法
APIView的dispatch方法中,首先通过initialize_request
方法将原来View的request的对象变成了drf的request对象,然后执行了认证、频率、权限,之后就是对类中方法的反射处理执行,并且对认证、频率、权限、类中方法的反射进行了异常捕获,处理,返回到response对象中。
总结
- 只要继承了APIView都没有csrf的认证了
- 以后视图类中使用的request对象,已经变成了drf提供了Request类的对象了
- 执行视图类的方法之前,执行了3大认证(认证、权限、频率)
- 在执行三大认证和视图类的方法过程中只要报错,都会被捕获处理
Request类源码分析
视图类中使用的request对象,已经变成了drf提供的Request类的对象了
class Books(APIView):
def get(self,request):
print('drf的request:',request)
print('原生的request:',request._request)
res=models.Book.objects.all()
book_list=[]
for i in res:
book_list.append({'title':i.title,'price':i.price,'publish':i.publish})
return Response(book_list)
request已经不是原来的request了,还能像原来的request一样使用吗?
由图Request中的__getattr__
可知,只要是旧的request有的属性或方法都可以使用。
print(request.GET) # get提交的数据
print(request.method) # 请求方法
print(request.path) # 路径
print(request.POST) # post请求提交的数据
Request的源码分析
__getattr__魔法方法
在rest_framework.request.Request类中查看,类中有个魔法方法:__getattr__
:对象点属性,属性不存在的时候会触发它的执行
如果取的属性不存在会去原生django的request对象中取出来
def __getattr__(self, attr):
"""
If an attribute does not exist on this instance, then we also attempt
to proxy it to the underlying HttpRequest object.
"""
try:
return getattr(self._request, attr) # self._request=request._request,就是原来的request
except AttributeError:
return self.__getattribute__(attr)
以后用的所有属性和方法,直接使用就可以,新的request会从原来的request中取
新的request内部有个老的request,就是
request._request # 老的request
data方法
data是一个方法,被装饰器property修饰了,变成了数据属性用。
以后从body体内提交的数据,都从这里取(request.POST)
- urlencoded,form-data:提交的数据堵在request.POST中
- json格式提交的数据,在request.POST中没有,它在request.body中
现在无论哪种格式,都从request.data中取
query_params方法
get请求提交的参数,等同于request._request.GET或request.GET
其他
取文件也是从request.FILES中取,跟之前一样
验证
原生request.POST只有urlencoded和formdata格式提交的数据,json提交的数据在body中,拿出来自己处理,但是drf的request中只有data,data中可以取到任意编码提交的数据
魔法方法
复习一下
序列化组件介绍
from rest_framework import serializers
序列化组件是drf提供的一个类,我们可以通过继承它,写自己的序列化类,用来序列化查询出来的queryset对象,可以帮我们简化很多代码,提供开发的效率。
新建一个serializer.py文件
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
title=serializers.CharField()
price=serializers.IntegerField()
publish=serializers.CharField()
在视图类中序列化查询结果并返回
from rest_framework.response import Response
from rest_framework import views
from app01 import models
from app01 import serializer
class Books(views.APIView):
def get(self,request,pk):
res=models.Book.objects.filter(pk=pk).first()
ser=serializer.BookSerializer(instance=res)
return Response(ser.data)
序列化组件的基本使用
查看所有图书,并序列化返回
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
title=serializers.CharField()
price=serializers.IntegerField()
publish=serializers.CharField()
from rest_framework.response import Response
from rest_framework import views
from app01 import models
from app01 import serializer
class Books(views.APIView):
def get(self,request,pk):
res=models.Book.objects.all()
ser=serializer.BookSerializer(instance=res,many=True)
return Response(ser.data)
反序列化
路由
urlpatterns = [
path('admin/', admin.site.urls),
path('books/',views.Books.as_view()),
path('booksdetail/<int:pk>/',views.BooksDetail.as_view())
]
自定义序列化类
from rest_framework import serializers
from app01 import models
class BookSerializer(serializers.Serializer):
title=serializers.CharField()
price=serializers.IntegerField()
publish=serializers.CharField()
# 重写create方法
def create(self, validated_data):
# validated_data表示校验过后的数据
res=models.Book.objects.create(**validated_data)
return res
# 重写update方法
def update(self, instance, validated_data):
instance.title=validated_data.get('title')
instance.price=validated_data.get('price')
instance.publish=validated_data.get('publish')
instance.save()
return instance
视图类
from rest_framework.response import Response
from rest_framework import views
from app01 import models
from app01 import serializer
class Books(views.APIView):
def get(self,request):
res=models.Book.objects.all()
ser=serializer.BookSerializer(instance=res,many=True)
return Response(ser.data)
def post(self,request):
# 前端传递数据,从request.data取出来
ser=serializer.BookSerializer(data=request.data)
if ser.is_valid(): # 表示校验前端传入的数据,在序列化类中没有写规则,等于没校验
ser.save() # 如果自定义的序列化类中没有写create方法,这里会报错,调用BaseSerializer的save方法,判断了,如果instance有值执行update,meiyou值执行create
return Response(ser.data)
else:
return Response(ser.errors)
# 单条数据
class BooksDetail(views.APIView):
def get(self,request,pk):
res=models.Book.objects.filter(pk=pk).first()
# instance表示要修改的实例
ser=serializer.BookSerializer(instance=res,data=request.data)
return Response(ser.data)
def put(self,request,pk):
res=models.Book.objects.filter(pk=pk).first()
# instance表示要修改的实例
ser=serializer.BookSerializer(instance=res,data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
作业
- 继承apiview写5个接口
serializer.py
from rest_framework import serializers
from app01 import models
class BookSerializer(serializers.Serializer):
title=serializers.CharField()
price=serializers.IntegerField()
publish=serializers.CharField()
def create(self, validated_data):
res=models.Book.objects.create(**validated_data)
return res
def update(self, instance, validated_data):
instance.title=validated_data.get('title')
instance.price=validated_data.get('price')
instance.publish=validated_data.get('publish')
instance.save()
return self.instance
view.py
"""
查询所有
查询单条
修改id为1
增加1条
删除id为1
"""
# 序列化
class Books(views.APIView):
def get(self,request):
res=models.Book.objects.all()
ser=serializer.BookSerializer(instance=res,many=True)
return Response(ser.data)
def post(self,request):
ser=serializer.BookSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
class BookDetail(views.APIView):
def get(self,request,pk):
res=models.Book.objects.filter(pk=pk).first()
ser=serializer.BookSerializer(instance=res)
return Response(ser.data)
def put(self,request,pk):
res=models.Book.objects.filter(pk=pk).first()
ser=serializer.BookSerializer(instance=res,data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
return Response(ser.errors)
def delete(self,request,pk):
models.Book.objects.filter(pk=pk).delete()
return Response('')
2.fbv写,写个装饰器,装饰在视图函数,只要一装饰,以后的request就可以使用,request.data,这个data无论是哪种编码格式,都有数据
class MyRequest(Request):
@property
def data(self):
if self._request.POST:
return {'post':self._request.POST}
return {'get':self._request.GET,'body':self._request.body,'files':self._request.FILES}
def request_decorator(func_name):
def inner(request,*args,**kwargs):
request=MyRequest(request)
print(request.data)
res=func_name(request,*args,**kwargs)
return res
return inner
@request_decorator
def book_fbv(request):
return JsonResponse({
'data':request.GET
})