第三篇:DRF之视图组件
第三篇:DRF之视图组件
一、请求和响应
1、请求
我们再看一遍源码,使用from rest_framework.request import Request
导入Request。内部实现如下所示。
下面,确保了可以按照原生的request取数据。
"""重要方法""" 【这里的request是新的request】
# request.data
前端以三种编码方式传入的数据,都可以取出来,是一个字典
# request.query_params
与Django标准的request.GET相同,只是更换了更正确的名称而已
# request.FILES
取出以formdata,传过来的文件数据,【其实在request.data也可以拿到】
2、响应
使用from rest_framework.response import Response
,查看源码。我们发现Response并不是只可以传入一个data,而是可以传很多数据。
# data
要返回的数据,一般以字典格式
# status
返回的状态码,默认是200【正确】
我们可以通过 from rest_framework import status 查看所有状态码
在这个路径下,它把所有使用到的状态码都定义成了常量
# template_name
渲染的模板名字(自定制模板),不需要了解【就是返回给浏览器的页面】
# headers
响应头,可以往响应头中放数据,就是一个字典。
【原生django以什么方式往响应头中存放数据?直接在httpresponse中以参数传入即可】
# exception
异常处理相关
# content_type
响应的编码格式,application/json和text/html;
【postman中是,application/json;而浏览器中是text/html】
那么引出一个问题,浏览器和postman是怎么显示不同的央视的呢?内部是如何区分的呢?
浏览器响应成浏览器的格式,而postman响应成json格式,是通过配置实现的(内部的默认配置便是如此),那么我们如何进行修改REST_FRAMEWORK的默认配置的呢?
3、配置REST_FRAMEWORK参数
1、全局配置
我们在settings.py
文件中,书写这样的配置。
# 配置参数
REST_FRAMEWORK = {
# 默认响应渲染类
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 浏览器API渲染器
)
}
如果希望浏览器返回json格式数据,我们只需要将第二栏注释掉即可。
2、局部配置
那么我们想要将某一个视图函数进行相关配置,那么就需要用到局部配置。
方式如下。
from rest_framework.renderers import JSONRenderer
# 测试渲染1 【如何只去掉这个视图类的浏览器渲染?】
class TestBro1(APIView):
renderer_classes = [JSONRenderer] # 局部配置
def get(self, request):
return Response(
data={'name': '杨毅', 'age': 18},
status=status.HTTP_200_OK,
headers={'test': '666'}
)
总结:drf的相关配置信息,先从自己类中找 ----> 项目的setting中找 ----> 到drf默认的配置文件中找。
二、视图组件
1、基于APIVIew写接口
我们观看一下以前基于APIVIew写的5个接口。【分别为 查看一个、查看所有、修改、新增、删除】。
"""urls.py"""
# 基于基于APIVIew写接口写的5个接口
url(r'^book/(?P<pk>\d+)/', views.BookView.as_view()), # 有名分组和无名分组之后必须传入一个参数,如pk
url(r'books/', views.BooksView.as_view())
"""ser.py"""
from rest_framework import serializers
from app01 import models
# 创建序列化组件
class BookSerializers(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__'
"""models.py"""
from django.db import models
# 创建书籍表
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
"""views.py"""
"""基于APIView实现5个接口"""
class BookView(APIView):
# 查看一个数据
def get(self, request, pk):
back_dic = {'status': 200, 'msg': '查看成功'}
# 根据pk拿到数据对象
book_obj = models.Book.objects.filter(id=pk).first()
# 序列化
book_ser = BookSerializers(book_obj)
# 返回数据
back_dic['data'] = book_ser.data
# 返回
return Response(back_dic)
# 修改一个数据
def put(self, request, pk):
back_dic = {'status': 200, 'msg': '修改成功'}
# 根据pk拿到数据对象
book_obj = models.Book.objects.filter(id=pk).first()
# 序列化【继承ModelSerializer不需要重写update方法】
book_ser = BookSerializers(instance=book_obj, data=request.data)
# 判断是否成功
if book_ser.is_valid():
# 返回数据
book_ser.save() # 保存数据
back_dic['data'] = book_ser.data
else:
back_dic['status'] = 101
back_dic['msg'] = '修改失败'
# 返回
return Response(back_dic)
# 删除一个数据
def delete(self, request, pk):
back_dic = {'status': 200, 'msg': '删除成功'}
# 根据pk拿到数据对象
models.Book.objects.filter(id=pk).delete()
return Response(back_dic)
class BooksView(APIView):
# 查看所有数据
def get(self, request):
back_dic = {'status': 200, 'msg': '获取全部数据成功'}
# 拿到所有的数据
book_queryset = models.Book.objects.all()
# 序列化
book_ser = BookSerializers(book_queryset, many=True)
# 添加数据
back_dic['data'] = book_ser.data
# 返回数据
return Response(back_dic)
# 新增一个数据
def post(self, request):
back_dic = {'status': 200, 'msg': '新增数据成功'}
# 操作【不需要书写create方法】
book_ser = BookSerializers(data=request.data)
if book_ser.is_valid():
book_ser.save() # 保存数据
back_dic['data'] = book_ser.data
else:
back_dic['status'] = 102
back_dic['msg'] = '新增数据失败'
# 返回数据
return Response(back_dic)
2、基于GenericAPIView写5个接口
我们虽然可以通过APIView实现5个接口,但是我们发现代码过于冗余,有没有更简便的方法呢?GenericAPIView进行了一定的优化。
具体代码如下。
- urls.py
# 基于GenericAPIView写的
url(r'^books2/(?P<pk>\d+)', views.BookDetailView2.as_view()),
url(r'^books2/', views.BookView2.as_view())
- views.py
from rest_framework.generics import GenericAPIView
"""基于GenericAPIView写的5个接口"""
# GenericAPIView ---> APIView ---> View ---> objects ---> type
class BookView2(GenericAPIView):
# 观看源码 queryset = models.Book.objects 也可以
queryset = models.Book.objects.all()
serializer_class = BookSerializers
# 获取所有的数据
def get(self, request):
# 代替 book_queryset = models.Book.objects.all()
book_queryset = self.get_queryset()
# 代替 book_ser = BookSerializers(book_queryset, many=True)
book_ser = self.get_serializer(book_queryset, many=True) # 本质还是执行上面
# 返回数据
return Response(book_ser.data)
# 新增一个数据
def post(self, request):
# 代替 book_ser = BookSerializers(data=request.data)
book_ser = self.get_serializer(data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({'status': 101, 'msg': '校验失败'})
class BookDetailView2(GenericAPIView):
queryset = models.Book.objects.all()
serializer_class = BookSerializers
# 获取一个数据
def get(self, request, pk):
# 代替 book_obj = models.Book.objects.filter(id=pk).first()
book_obj = self.get_object() # 得到一个数据对象
# 代替 book_ser = BookSerializers(book_obj)
book_ser = self.get_serializer(book_obj)
# 返回数据
return Response(book_ser.data)
# 修改一个数据
def put(self, request, pk):
# 代替 book_obj = models.Book.objects.filter(id=pk).first()
book_obj = self.get_object()
# 代替 book_ser = BookSerializers(instance=book_obj, data=request.data)
book_ser = self.get_serializer(instance=book_obj, data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({'status': 101, 'msg': '校验失败'})
# 删除一个数据
def delete(self, request, pk):
# 代替 models.Book.objects.filter(id=pk).delete()
self.get_object().delete()
return Response({'status': 100, 'msg': '删除成功'})
总结:
"""视图类开始"""
# 拿到所有数据的查询集
queryset = models.Book.objects.all()
# 拿到序列化的类
serializer_class = BookSerializers
"""操作"""
# 拿到所有的数据
book_queryset = self.get_queryset()
# 拿到一个数据对象
book_obj = self.get_object() # 不需要传参,会自动使用pk
# 序列化方式统一
self.get_serializer() === BookSerializers()
3、基于GenericAPIView和5个视图扩展类写的接口
现在我们已经可以将代码进行所谓的"工整化",但是代码仍然太过于冗余,我们怎么再进一步进行简化?这就要用到了5个视图扩展类
,具体使用方式如下。
- urls.py
# 基于GenericAPIView和5个视图扩展类
url(r'^books3/(?P<pk>\d+)', views.BookDetailView3.as_view()),
url(r'^books3/', views.BookView3.as_view())
- views.py
"""基于GenericAPIView和5个视图扩展类写的接口""" # 5个视图扩展类每个写了一个方法【如:list】
from rest_framework.mixins import ListModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin
class BookView3(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = models.Book.objects.all()
serializer_class = BookSerializers
# 获取所有的数据【将上面基于GenericAPIView的代码再次进行封装】
def get(self, request):
return self.list(request)
# 新增一个数据
def post(self, request):
return self.create(request)
class BookDetailView3(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = models.Book.objects.all()
serializer_class = BookSerializers
# 获取一个数据
def get(self, request, pk):
return self.retrieve(request, pk)
# 新增一个数据
def put(self, request, pk):
return self.update(request, pk)
# 删除一个数据
def delete(self, request, pk):
return self.destroy(request, pk)
4、基于GenericAPIView同级的9个视图类
到了这里,我们可以更进一步进行封装,方式如下。
- urls.py
# 基于GenericAPIView同级的9个视图类
url(r'^books4/(?P<pk>\d+)', views.BookDetailView4.as_view()),
url(r'^books4/', views.BookView4.as_view())
- views.py
"""基于GenericAPIView同级的9个视图类""" # 继承了GenericAPIView+一个两个或三个视图扩展类,同时内部书写了相关的请求方法
from rest_framework.generics import CreateAPIView,ListAPIView,UpdateAPIView,RetrieveAPIView,DestroyAPIView,ListCreateAPIView,RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView,RetrieveUpdateAPIView
class BookView4(ListAPIView, CreateAPIView):
"""观看源码,发现继承的类中已经有了相关的操作,做了更一层的覆盖"""
queryset = models.Book.objects.all()
serializer_class = BookSerializers
class BookDetailView4(RetrieveAPIView, UpdateAPIView, DestroyAPIView):
queryset = models.Book.objects.all()
serializer_class = BookSerializers
5、基于ModelViewSet实现5个接口
到了这里,还没有完,我们应该想一下,如何将两个视图类,拼成一个视图类,我们发现最难的问题是,两个类中都有get方法,同时,路由配置也是一个问题,那么我们该如何解决呢?这就引入了ModelViewSet的使用。
- urls.py
"""基于ModelViewSet实现5个接口"""
url(r'^books5/(?P<pk>\d+)', views.BookView5.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'})),
# 当路径匹配,又是get请求,会执行BookView5的list方法
url(r'^books5/', views.BookView5.as_view(actions={'get':'list','post':'create'}))
- views.py
"""基于ModelViewSet实现5个接口"""
from rest_framework.viewsets import ModelViewSet
class BookView5(ModelViewSet): # 我们点击去发现,5个接口都有,也就是一个多继承而已,但是路由有点问题。
queryset = models.Book.objects.all()
serializer_class = BookSerializers
我们如何理解上面的代码呢?
所以我们可以在views.py
中更简单的进行书写。但是路由配置如何解决,我们再观察源码。
继承关系是ModelViewSet ----> GenericViewSet ----> ViewSetMixin
,而在ViewSetMixin中重写了as_view()方法。我们进行进行查看。
由此,我们将路由进行actions参数的配置,即可解决路由冲突问题。
6、案例:继承ViewSetMixin的视图类
那么我们有了ViewSetMixin,我们可以延伸出什么东西呢?实际上,我们可以实现视图类不用get、post、delete、put...
等作为类中的函数名,用自定义的函数名进行代替。
具体实现方式如下。
- urls.py
# 基于ModelViewSet实现5个接口
url(r'^books6/', views.BookView6.as_view(actions={'get': 'get_all_book'}))
- views.py
from rest_framework.viewsets import ViewSetMixin
# ViewSetMixin一定放在APIView之前,因为类的继承关系关系是深度优先算法,继承的两个类中均有as_view()方法。
class BookView6(ViewSetMixin, APIView):
def get_all_book(self, request):
book_list = models.Book.objects.all()
book_ser = BookSerializers(book_list, many=True)
return Response(book_ser.data)