美文网首页Python
django的viewSet笔记

django的viewSet笔记

作者: 陈卧虫 | 来源:发表于2018-10-30 17:07 被阅读438次

    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))
    ]
    

    相关文章

      网友评论

        本文标题:django的viewSet笔记

        本文链接:https://www.haomeiwen.com/subject/ypbgtqtx.html