参考文章:刘清政《drf-视图组件》,原文链接:http://liuqingzheng.top/python/Django-rest-framework%E6%A1%86%E6%9E%B6/4-drf-%E8%A7%86%E5%9B%BE%E7%BB%84%E4%BB%B6/
一、rest_framework视图组件
Django REST framwork 提供的视图的主要作用:
- 控制序列化器的执行(检验、保存、转换数据)
- 控制数据库查询的执行
1.1、响应方式
上节学习中,我们发现当请求来自于浏览器时,rest_framework响应携带了HTML页面;当请求来自于postman时,只有json数据,这是怎么做到的呢?
其实是rest_framework会根据HTTP request里面的user-agent来判断是否为浏览器,这个配置是可以改的,且分全局和局部配置!
局部
仅对某个视图类有效
from rest_framework.renderers import JSONRenderer # 先导入对应模块
renderer_classes = [JSONRenderer, ] # 在视图类中加入此配置即可配置该视图类返回的一定是json数据而不带HTML页面。
全局
所有视图类都有效,在settings配置,里面都是drf的配置信息。
drf有自己的默认配置文件,查找顺序是先从类中找接着从项目的settings找,都没有再用自己默认的;后面的认证权限、频率校验等等都是在全局或者局部中配置。
# from rest_framework import settings # drf默认配置文件
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer', # 需要全局生效JSON还是带HTML,只保留对应配置,其余的注释掉即可。
# 'rest_framework.renderers.BrowsableAPIRenderer',
]
}
1.2、视图基类
REST framework 提供了众多的通用视图基类与扩展类,以简化视图的编写。
1.2.1、APIView
我们上节学习就是用的APIView,APIView
是REST framework提供的所有视图的基类,继承自Django的View
父类。视图类中的get、put等方法自己纯手写,有没有感觉很繁琐?来,看看下面的兄弟。
1.2.2、GenericAPIView
继承自APIVIew
,主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。通常在使用时,可搭配一个或多个Mixin扩展类。
它简化了我们使用APIView时纯手写的方法,一起来看看。
通用视图类
GenericAPIView源码一开始就要定义的两个参数
queryset = None # queryset对象,查询所有。
serializer_class = None # 要使用的序列化类
先看看以前定义的视图类中的get方法代码吧
from rest_framework.generics import GenericAPIView # 导入GenericAPIView
class ArticlesView(APIView):
def get(self, request):
article = models.Article.objects.all()
article_obj = ArticleSeriallizer(instance=article, many=True)
return Response(article_obj.data)
GenericAPIView里面定义了很多方法,可以完美替代我们之前手写的get、post等方法,解放双手!!!因直接贴源码篇幅太长,所以我这里只放出对应的方法和该方法的返回值,能看懂就行。
def get_queryset(self)
...
return queryset # 这不就是我们之前手写的:article = models.Article.objects.all() 嘛!!!
def get_serializer_class(self):
...
return self.serializer_class # 这不就是之前手写的:article_obj = ArticleSeriallizer(instance=article, many=True) 嘛!!!
所以之前的所有方法可以改成下面这样了,主要是用self.get_object()替换models.Article.objects.filter(pk=pk).first;用self.get_queryset()替换models.Article.objects.all();用self.get_serializer_class()替换ArticleSeriallizer()!弄好一个,所有方法直接复制黏贴就行了,开心不!
class ArticleView(GenericAPIView):
queryset = models.Article.objects.all() # queryset对象,查询所有。
serializer_class = ArticleSeriallizer # 要使用的序列化类
def get(self, request, pk):
article = self.get_object()
article_obj = self.get_serializer_class(instance=article)
return Response(article_obj.data)
def put(self, request, pk):
callback_dict = {'status': 201, 'msg': 'sucess'}
article = self.get_object()
article_obj = self.get_serializer_class(instance=article, data=request.data)
if article_obj.is_valid():
article_obj.save()
callback_dict['data'] = article_obj.data
else:
callback_dict['status'] = 400
callback_dict['msg'] = 'invaild data'
callback_dict['data'] = article_obj.errors
return Response(callback_dict)
def post(self, request):
callback_dict = {'status': 201, 'msg': 'sucess'}
article = self.get_serializer_class(data=request.data)
if article.is_valid():
article.save()
callback_dict['data'] = article.data
else:
callback_dict['status'] = 400
callback_dict['msg'] = 'invaild data'
callback_dict['data'] = article.errors
return Response(callback_dict)
def delete(self, request, pk):
response_dict = CallBackDict()
delete_obj = self.get_object().delete()
return Response(response_dict.callback_dict)
class ArticlesView(APIView):
queryset = models.Article.objects.all() # queryset对象,查询所有。
serializer_class = ArticleSeriallizer # 要使用的序列化类
def get(self, request):
article = self.get_queryset()
article_obj = self.get_serializer_class(instance=article, many=True)
return Response(article_obj.data)
你以为GenericAPIView就这点本事吗!太小看它了,来点有意思的。
5个视图扩展类
在views先导入以下模块
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin
先看看ListModelMixin的源码,熟悉不?这不就是我们之前手写的方法!以此类推,从左到右为:查询所有、新增、修改、删除、查询单个
class ListModelMixin:
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
所以,以上手写的所有方法,在继承了五个视图子类后,可以简化成这样:
# 有没有感觉!这代码量少了好多!
class ArticleView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, CreateModelMixin, DestroyModelMixin):
queryset = models.Article.objects.all() # queryset对象,查询所有。
serializer_class = ArticleSeriallizer # 要使用的序列化类
def get(self, request, pk):
return self.retrieve(request, pk) # 来自RetrieveModelMixin
def put(self, request, pk):
return self.update(request, pk) # 来自UpdateModelMixin
def post(self, request):
return self.create(request) # 来自CreateModelMixin
def delete(self, request, pk):
return self.destroy(request, pk) # DestroyModelMixin
class ArticlesView(APIView, ListModelMixin):
queryset = models.Article.objects.all() # queryset对象,查询所有。
serializer_class = ArticleSeriallizer # 要使用的序列化类
def get(self, request):
return self.list(request) # 来自ListModelMixin
上面通过继承五个子类后,我们的代码量得到大大精简,但是观察一下自己写的ArticleView和ArticlesView,会发现都有get方法重复了,虽然查询的是不一样的结果,能不能再封装一下呢?答案是有!
9个视图子类
视图子类 | 提供的方法 | 继承自 |
---|---|---|
CreateAPIView | post | GenericAPIView、CreateModelMixin |
ListAPIView | get(get所有) | GenericAPIView、ListModelMixin |
RetrieveAPIView | get(get单个) | GenericAPIView、RetrieveModelMixin |
DestoryAPIView | delete | GenericAPIView、DestoryModelMixin |
UpdateAPIView | put、patch | GenericAPIView、UpdateModelMixin |
RetrieveUpdateAPIView | get、put、patch | GenericAPIView、RetrieveModelMixin、UpdateModelMixin |
RetrieveUpdateDestoryAPIView | get、put、patch、delete | GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin |
ListCreateAPIView | get、post | ListModelMixin、CreateModelMixin、GenericAPIView |
RetrieveDestroyAPIView | get(单个)、delete | RetrieveModelMixin、DestroyModelMixin、GenericAPIView |
所以,上面的ArticleView类的代码还可以写成这样
没错,就是这么三行代码就完事!膜拜大佬!
class ArticleView(RetrieveUpdateDestoryAPIView, CreateAPIView)
queryset = models.Article.objects # queryset对象
serializer_class = ArticleSeriallizer # 要使用的序列化类