视图组件
视图组件
基本视图
路由
url(r'^publish/$', views.PublishView.as_view()),
url(r'^publish/(?P<pk>\d+)/',views.PublishDetailView.as_view()),
serializer.py
from app01 import models
class PublishSerializers(serializers.ModelSerializer):
class Meta:
model=models.Publish
fields='__all__'
view.py
class PublishView(APIView):
def get(self, request):
publish_list = models.Publish.objects.all()
bs = PublishSerializers(publish_list, many=True)
# 序列化数据
return Response(bs.data)
def post(self, request):
# 添加一条数据
print(request.data)
bs=PublishSerializers(data=request.data)
if bs.is_valid():
bs.save() # 生成记录
return Response(bs.data)
else:
return Response(bs.errors)
class PublishDetailView(APIView):
def get(self,request,pk):
publish_obj=models.Publish.objects.filter(pk=pk).first()
bs=PublishSerializers(publish_obj,many=False)
return Response(bs.data)
def put(self,request,pk):
publish_obj = models.Publish.objects.filter(pk=pk).first()
bs=PublishSerializers(data=request.data,instance=publish_obj)
if bs.is_valid():
bs.save() # update
return Response(bs.data)
else:
return Response(bs.errors)
def delete(self,request,pk):
models.Publish.objects.filter(pk=pk).delete()
return Response("")
mixin类和generice类编写视图
ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
五个类中封装了查所有、新创 | 查一个、更新、删除 五种方法
但是在这五个方法中用到的get_serializer、get_object等方法均在GenericAPIView类中
而GenericAPIView类需要queryset或者serializer_class参数
因此我们只需要让视图类继承上述五个类和GenericAPIView类组合即可实现简单的增删改查的接口
views.py
from rest_framework.mixins import CreateModelMixin,RetrieveModelMixin,ListModelMixin,UpdateModelMixin,DestroyModelMixin
from rest_framework.generics import GenericAPIView
class PublishView(ListModelMixin,CreateModelMixin,GenericAPIView):
queryset=models.Publish.objects.all()
serializer_class=PublishSerializers
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
class PublishDetailView(RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView):
queryset=models.Publish.objects.all()
serializer_class=PublishSerializers
def get(self,request,*args,**kwargs):
return self.retrieve(request,*args,**kwargs)
def put(self,request,*args,**kwargs):
return self.update(request,*args,**kwargs)
def delete(self,request,*args,**kwargs):
return self.destroy(request,*args,**kwargs)
这个时候,我们只需要提供queryset和serializer_class两个参数配置,mixin包下面的类会帮我们处理数据,我们调用对应的方法并且将其返回值返回即可,
但是需要注意的是,如果使用此方法,urls.py的url对应的id要命名为pk,如下:
url(r'^publishes/$', views.PublishView.as_view()),
url(r'^publishes/(?P<pk>\d+)/$', views.PublishDetailView.as_view()),
但是,即使我们用了这种封装,很多代码还是有重复的,所有,rest_framework又给我们做了一层封装
使用generics 下ListCreateAPIView,RetrieveUpdateDestroyAPIView
ListCreateAPIView类
继承了ListModelMixin,CreateModelMixin和GenericAPIView
在类体代码中实现了get 和 post 方法 并返回了序列化结果
RetrieveUpdateDestroyAPIView类
继承了RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin和GenericAPIView
在类体代码中实现了get 、 (put、patch)、delete方法 并返回了序列化结果
所以有如下写法
from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView
class PublishView(ListCreateAPIView):
queryset=models.Publish.objects.all()
serializer_class=PublishSerializers
class PublishDetailView(RetrieveUpdateDestroyAPIView):
queryset=models.Publish.objects.all()
serializer_class=PublishSerializers
这样,代码就清晰很多了,但是,这种方法依然是将一个model表分成两个视图,那,有没有一种方法能将他们合并在一起呢?
使用ModelViewSet
路由:
url(r'^publish/$', views.PublishView.as_view({'get':'list','post':'create'})),
url(r'^publish/(?P<pk>\d+)/$', views.PublishView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
views.py
from rest_framework.viewsets import ModelViewSet
class PublishView(ModelViewSet):
queryset=models.Publish.objects.all()
serializer_class=PublishSerializers
自定义方法与不同请求方式对应
url.py
url(r'^aa/$', views.PublishView.as_view({'get': 'aaa'})),
url(r'^bb/$', views.PublishView.as_view({'get': 'bbb'})),
views.py
from rest_framework.viewsets import ViewSetMixin
from rest_framework.views import APIView
# ViewSetMixin 重写了as_view方法
class Test(ViewSetMixin,APIView):
def aaa(self,request):
return Response()
def bbb(self,request):
return Response()
源码分析
普通的CBV中的视图类as_view()是无法传参的,ViewSetMixin 重写了as_view方法
rest_framework>viewsets>ViewSetMixin
部分源码如下:
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
"""
Because of the way class based views create a closure around the
instantiated view, we need to totally reimplement `.as_view`,
and slightly modify the view function that is created and returned.
"""
......
# 若继承了ModelViewSet类必须在路由中传入actions
if not actions:
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`")
......
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# We also store the mapping of request methods to actions,
# so that we can later set the action attribute.
# eg. `self.action = 'list'` on an incoming GET request.
self.action_map = actions
# Bind methods to actions
# This is the bit that's different to a standard view
# 将视图类中的action方法与请求方式绑定
# 不同的请求方式对应出发不同的方法
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
# 请求头相关的
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# And continue as usual
return self.dispatch(request, *args, **kwargs)
主键中各个类的继承关系
各个类关系分析
首先我们得了解一下,当我们不去自己继承View类定义类视图时,DRF给哦们提供了几个继承自View类的丰富的类
基类:APIView
APIView是REST framework提供的所有视图的基类,继承自Django的View父类。
APIView与View的不同之处在于:
传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象;
视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式;
任何APIException异常都会被捕获到,并且处理成合适的响应信息;
在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
支持定义的属性:
authentication_classes 列表或元祖,身份认证类
permissoin_classes 列表或元祖,权限检查类
throttle_classes 列表或元祖,流量控制类
在APIView
中仍和普通的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。
2.基类:GenericAPIView
提供的关于序列化器使用的属性与方法
GenericAPIView
继承自APⅣew,主要增加了操作序列化器和数
据库查询的方法,作用是为下面Mixn扩展类的
执行提供方法支持。通常在使用时,可搭配一个
或多个Min扩展类
指明视图使用的序列化器
1通过属性: serializer_ class
2通过方法: get serializer_class(self
get serializer(self, args, * kwargs)
数据库查询的属性与方法
指明使用的数据查询集
1通过属性: queryset
2通过方法: get queryset(se)
get object(self)
配合GenericAPIView使用的几个扩展类
我们发现前边的两个父类还需要进行函数的定义去处理不同的请求,这样我们还得在函数里边实现查询集以及序列化器的指定,工作量仍然很大。怎么解决呢?
GenericAPIView的五个扩展类给我们提供了五个方法分别进行增删改查的不同操作,这样我们就不用写那么多函数啦!!
搭配GenericAPIView使用
1.ListModelMixin: 提供list方法快速实现列表视图
2.CreateModelMixin: 提供create方法快速实现创建资源的视图
3.RetrieveModelMixin 提供retrieve方法,可以快速实现返回一个存在的数据对象(需要传入pk)
4.UpdateModelMixin 提供update方法,可以快速实现更新一个存在的数据对象。 提供partial_update方法,可以实现局部更新
5.DestroyModelMixin 提供destroy方法,可以快速实现删除一个存在的数据对象
1.CreateAPIView(等价于GenericAPIView+CreateModelMixin) 提供 post 方法 继承自: GenericAPIView、CreateModelMixin
2.ListAPIView 提供 get 方法 继承自:GenericAPIView、ListModelMixin
3.RetrieveAPIView 提供 get 方法 继承自: GenericAPIView、RetrieveModelMixin
4.DestoryAPIView 提供 delete 方法 继承自:GenericAPIView、DestoryModelMixin
5.UpdateAPIView 提供 put 和 patch 方法 继承自:GenericAPIView、UpdateModelMixin
6.RetrieveUpdateAPIView 提供 get、put、patch方法 继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
7.RetrieveUpdateDestoryAPIView 提供 get、put、patch、delete方法 继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
所以代码中的mixins.RetrieveModelMixin, GenericAPIView可以写成RetrieveAPIView