drf之视图集
一、为什么要有视图集
我们使用drf提供的generics包可以很轻松的实现六大基础接口,但我们会发现群查和单查却无法共存。这是由于我们寄希望于继承generics.ListAPIView来实现群查,同时有希望继承generics.RetrieveAPIView来实现单查,但由于继承顺序的关系,群查和单查并不能共存。
我们希望在访问 群查或单查的接口时,可以有另外一种映射关系。
""" ViewSetMixin类存在理由推到
1)工具视图类,可以完成应付六大基础接口,唯一缺点是单查与群查接口无法共存
(只配置queryset、serializer_class、lookup_field)
2)不能共存的原因:RetrieveAPIView和ListAPIView都是get方法,不管带不带pk的get请求,只能映射给一个get方法
3)能不能修改映射关系:
比如将/books/的get请求映射给list方法,
将/books/(pk)/的get请求映射给retrieve方法,
甚至可以随意自定义映射关系
"""
二、视图集
ViewSetMixin类是视图集的基类。它重写了as_view方法,rest_framework.viewsets下的类都间接或直接的继承了它,所以都会继承它的方法。
源码分析
def as_view(cls, actions=None, **initkwargs):
'''.......'''
# 必须提供actions
if not actions:
raise TypeError('....')
'''........'''
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.action_map = actions
# {'get':'list'}获取请求方式method--->映射方法list
for method, action in actions.items():
handler = getattr(self, action)
# 修改了映射get-->list
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
# 还是由dispatch进行分发
return self.dispatch(request, *args, **kwargs)
view.cls = cls
view.initkwargs = initkwargs
view.actions = actions
return csrf_exempt(view)
urls.py
我们看urls.py中的变化再看as_view的源码,两条url的as_view的actions参数是不一样的。
# 群查接口的as_view的actions
url(r'^v3/books/$', views.BookV3APIView.as_view(
{'get': 'list', 'post': 'create'}
)),
# 单查接口的as_view的actions
url(r'^v3/books/(?P<pk>\d+)/$', views.BookV3APIView.as_view(
{'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'}
)),
views.py
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
class BookV3APIView(ModelViewSet):
queryset = models.Book.objects.filter(is_delete=False).all()
serializer_class = serializers.BookModelSerializer
可以在ModelViewSet的源码中看到继承了一系列generic包下的一系列类。我们可以借用它直接完成六大接口!
三、路由组件
可以看到as_view中自己配置的actions的对应关系也比较繁琐,drf还为我们提供了路由组件,来配合视图集使用(路由组件不能单独存在,必须配合视图集使用)
urls.py
from django.conf.urls import url, include
from . import views
# 路由组件,必须配合视图集使用
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
# 以后就写视图集的注册即可:BookV3APIView和BookV4APIView都是视图集
router.register('v3/books', views.BookV3APIView, 'book')
router.register('v4/books', views.BookV4APIView, 'book')
urlpatterns = [
url('', include(router.urls))
]
views.py
from rest_framework.viewsets import ReadOnlyModelViewSet
class BookV4APIView(ReadOnlyModelViewSet):
queryset = models.Book.objects.filter(is_delete=False).all()
serializer_class = serializers.BookModelSerializer
四、自定义路由组件
router.py
from rest_framework.routers import SimpleRouter as DrfSimpleRouter
from rest_framework.routers import Route, DynamicRoute
class SimpleRouter(DrfSimpleRouter):
routes = [
# List route.
Route(
url=r'^{prefix}{trailing_slash}$',
mapping={
'get': 'list',
'post': 'create', # 注:群增只能自己在视图类中重写create方法,完成区分
'delete': 'multiple_destroy', # 新增:群删
'put': 'multiple_update', # 新增:群整体改
'patch': 'multiple_partial_update' # 新增:群局部改
},
name='{basename}-list',
detail=False,
initkwargs={'suffix': 'List'}
),
# Dynamically generated list routes. Generated using
# @action(detail=False) decorator on methods of the viewset.
DynamicRoute(
url=r'^{prefix}/{url_path}{trailing_slash}$',
name='{basename}-{url_name}',
detail=False,
initkwargs={}
),
# Detail route.
Route(
url=r'^{prefix}/{lookup}{trailing_slash}$',
mapping={
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
},
name='{basename}-detail',
detail=True,
initkwargs={'suffix': 'Instance'}
),
# Dynamically generated detail routes. Generated using
# @action(detail=True) decorator on methods of the viewset.
DynamicRoute(
url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$',
name='{basename}-{url_name}',
detail=True,
initkwargs={}
),
]