视图集有四个封装类,ViewSet基础封装、GenericViewSet进一步封装查询集和序列化器、ModelViewSet进一步封装action、ReadOnlyModelViewSet提取只读action。以下是展开介绍。
# 源码
class ViewSet(ViewSetMixin, views.APIView):
pass
没错,源码中ViewSet就是只有继承而没有实现任何方法&定义任何属性,我们对APIView已经不陌生了,我上一篇DRF类视图中有,它主要实现请求,响应,验证,异常,限流等。主要看ViewSetMixin类。
class ViewSetMixin(object):
"""
Overrides `.as_view()` so that it takes an `actions` keyword that performs
the binding of HTTP methods to actions on the Resource.
For example
view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
重写了as_view方法,它需要执行一个`actions`关键字
HTTP方法绑定到资源上的操作
"""
def as_view(cls, actions=None, **initkwargs)
# 有兴趣可以具体看实现,这里就不复制了
pass
由源码可以看出,仅仅提供了as_view方法将请求方法绑定到了actions关键字上,并没有给出actions的具体实现,所以我们在使用ViewSet的时候,需要自己定义出action方法(查询集和序列化器需要手动实例化调用、异常抛出等),然后通过as_view绑定请求方法即可。
# 举例1.1
# 继承视图集ViewSet
class BooksAPIViewSet(ViewSet):
# 定义action中的list实现查询所有
def list(self, request):
# 手动实现查询集
books = BookInfo.books.all()
# 手动使用序列化器
serializer = BookInfoModelSerializer(books, many=True)
# 返回响应,APIView中实现的Response响应对象
return Response(serializer.data)
# 定义action中的retrieve检索条件
def retrieve(self, request, pk):
try:
book = BookInfo.books.get(id=pk)
except BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = BookInfoSerializer(book)
return Response(serializer.data)
我们实现了视图集,为了使他生效,我们还需要指定他们的url。
# as_view中实现了get->list的绑定,是我们发送的get /books/请求能够生效
url(r'^books/$', views.BooksAPIViewSet.as_view({'get': 'list'})),
# 绑定get->retrieve,使得get /books/id/生效
url(r'^books/(?P\d+)/$' , views.BooksAPIViewSet.as_view({'get': 'retrieve'})),
综上:继承ViewSet,需要自定义action,action中需要自己实现查询集和序列化器,url中as_view需要绑定方法和action的对应关系
# 源码
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
pass
同样GenericViewSet类中也没有任何实现,全部继承自ViewSetMixin和GenericAPIView。DRF类视图中有GenericAPIView的介绍,它主要实现指定查询集queryset/get_queryset/get_object和指定序列化器serializer_class/get_serializer_class等。ViewSetMixin类刚刚提过主要重写实现了as_view()用于request方法和action的绑定。注意:GenericViewSet一般需要和5种扩展类执行。
# 举例1.2
# 继承视图集GenericViewSet
class BooksAPIViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
# 指定查询集和序列化器
# 之所以不用定义get/post方法,是因为重写了as_view方法,将get映射为了list方法
# 之所以不用第一action,是因为继承了ListModelMixin等扩展类
queryset = BookInfo.books.all()
serializer_class = BookInfoModelSerializer
# 源码
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
pass
同样,我们的ModelViewSet中也没有任何功能的具体实现,单纯的继承五个扩展类的功能和GenericViewSet的功能。很明显,五个扩展类主要实现了Create(增)List/Retrieve(查)Update(改)Destroy(删)。GenericViewSet主要实现了序列化器的指定,查询集的指定,和as_view的重写。
# 源码
class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
GenericViewSet):
pass
由此可以看出ReadOnlyModelViewSet仅仅是查询集。