ViewSet: 视图集
作用: 做整合, 解决查询资源时, 单个和多个资源的整合问题
现在我们定义一个视图, 用于查询书籍的数据
单个数据的类视图
class BookListAPIView(mixins.ListModelMixin, GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request, pk): # 包含一个 PK
return self.list( request, pk) # 其实应该是args, kwargs, 只是想突出 PK 才这么写
多个数据的类视图
class BookDetailAPIView(mixins.RetrieveModelMixin, GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request):
return self.retrieve( request ) # 应该还有 *args, **kwargs
urls.py
urlpatterns = [
url(r'books/$', views.BookListAPIView.as_view()),
url(r'books/(?P<pk>\d+)/$', views.BookDetailAPIView.as_view()),
]
是不是很麻烦, 针对同个资源的查询, 只是数量不同, 却需要定义两个不同的类视图, 太过冗余
所以为了解决这个问题,视图集出现了, 它的作用就是将对同一资源的不同请求方式整合到一个视图当中.
使用视图集
class BookInfoViewSet(viewsets.ViewSet):
def list(self, request):
...
def retrieve(self, request, pk=None):
...
urls.py
urlpatterns = [
url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}),
url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'})
]
ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action , 通过as_view方法
将请求方式与行为(actions)的映射进行完成
这种映射是通过重写as_view()
方法实现的
class ViewSetMixin(object):
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
if not actions: # 如果没有传递actions参数,就会报错
raise TypeError("...")
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.action_map = actions # action= {'get': 'list', 'post': 'update'}
# 对字典actions ,进行映射
# 比如{'get':'list'}
# 就会拿到实例的 list方法,添加(或替换)掉get方法
for method, action in actions.items(): # 遍历actions中的所有方法
handler = getattr(self, action) # 取出视图里定义的函数的内存地址
setattr(self, method, handler) # 为该类视图设置新的属性,或者说添加新的方法, 这个方法的名字就是 method
# 在调用get方法的时候,实际上调用了list方法
return self.dispatch(request, *args, **kwargs) # 调用dispatch 让子类 使用 dispatch找到 请求方式对应的方法
return csrf_exempt(view) # 返回这个函数
视图集中附加action的方法
通过action装饰器
- methods: 该action支持的请求方式,列表传递
- detail: 表示是action中要处理的资源的对象的数量(即是否通过url路径获取主键)
- True 表示使用通过URL获取的主键, 即是单个数据对象
- False 表示不使用URL获取主键, 即获取的是一组数据对象
举例:
views.py
from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet
from rest_framework.decorators import action
class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
# detail为False 表示不需要处理具体的BookInfo对象
@action(methods=['get'], detail=False)
def latest(self, request):
"""
返回最新的图书信息
"""
book = BookInfo.objects.latest('id')
serializer = self.get_serializer(book)
return Response(serializer.data)
# detail为True,表示要处理具体与pk主键对应的BookInfo对象
@action(methods=['put'], detail=True)
def read(self, request, pk):
"""
修改图书的阅读量数据
"""
book = self.get_object()
book.bread = request.data.get('read')
book.save()
serializer = self.get_serializer(book)
return Response(serializer.data)
urls.py
urlpatterns = [
url(r'^books/$', views.BookInfoViewSet.as_view({'get': 'list'})),
url(r'^books/(?P<pk>\d+)/$', views.BookInfoViewSet.as_view({'get': 'retrieve'})),
# 没有pk的, 代表访问的的是多个资源,一组数据, 所以detail应该为False
url(r'^books/latest/$', views.BookInfoViewSet.as_view({'get': 'latest'})),
# 有pk的, 代表访问的的是单个资源, 所以detail应该为True
url(r'^books/(?P<pk>\d+)/read/$', views.BookInfoViewSet.as_view({'put': 'read'})),
]
Router路由的定义
上面的url的是不是太麻烦了, 可能感觉不出来, 因为只有4种请求方式, 但如果有100种呢, 是不是需要定义100个url,
太麻烦了, 所以引入router, 目的只有一个: 简化/省略 url的配置
- 内置两个可用的路由
- SimpleRouter
- DefaultRouter (继承自SimpleRouter, 在它的基础上添加了对根路由的支持)
- 使用方法
创建router对象,并注册视图集
from rest_framework import routers
router = routers.SimpleRouter()
router.register(r'books', BookInfoViewSet, base_name='book')
register(prefix, viewset, base_name)
- prefix 该视图集的路由前缀
- viewset 视图集
- base_name 路由名称的前缀
如上述代码会形成的路由如下:
^books/$ name: book-list
^books/{pk}/$ name: book-detail
^books/latest/$ name: book-latest
^books/{pk}/read/$ name: book-read
添加路由数据
可以有两种方式:
urlpatterns = [
...
]
urlpatterns += router.urls
或
urlpatterns = [
...
url(r'^', include(router.urls))
]
网友评论