August Rush

一个还在努力成长的小火汁!

游龙当归海,海不迎我自来也。

We create our own demons.

You can reach me at augustrush0923@gmail.com
DRF视图集
发布:2021年09月14日 | 作者:augustrush | 阅读量: 316

视图集

Django REST framework允许将一组相关视图的逻辑组合到一个称为ViewSet的类中。

ViewSet类只是一种基于类的View,它不提供任何处理方法,如.get().post(),而是提供诸如.list().create()之类的操作。

通常,不是在urlconf中的视图集中明确注册视图,而是使用路由器类注册视图集,这会自动确定urlconf。

使用ViewSet类比使用View类有两个主要优点。

  • 重复的逻辑可以合并成一个类。
  • 通过使用routers,不再需要处理自己的URL配置。


ViewSet

继承自APIView,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。

ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典的映射处理工作。

在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。

使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中。

ViewSet视图集类不再实现get()、post()等方法,而是实现动作action如list()、create()等。

ViewSet默认应该有如下几种动作:

  • list():提供一组数据
  • retrieve():提供单个数据
  • create():创建数据
  • update():保存数据
  • destory():删除数据

如果自身逻辑上述action无法满足,可以通过自定义方法。

class BooksInfoAPIView(ViewSet):
    def list(self, request):
        books = BooksInfo.objects.all()
        serializer = BookInfoModelSerializer(instance=books, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk):
        try:
            book = BooksInfo.objects.get(pk=pk)
        except BooksInfo.DoesNotExist:
            return Response('404 not found', status=HTTP_404_NOT_FOUND)
        serializer = BookInfoModelSerializer(instance=book)
        return Response(serializer.data)

    def create(self, request):
        data = request.data
        serializer = BookInfoModelSerializer(data=data)
        if not serializer.is_valid():
            return Response(serializer.errors)
        serializer.save()
        return Response(serializer.validated_data)

    def update(self, request, pk):
        data = request.data
        book = BooksInfo.objects.get(id=pk)
        serializer = BookInfoModelSerializer(instance=book, data=data, partial=True)
        if not serializer.is_valid():
            return Response(serializer.errors)
        serializer.save()
        return Response(serializer.validated_data)

    def destroy(self, request, pk):
        book = BooksInfo.objects.get(id=pk)
        book.delete()
        return Response("删除成功")

在设置路由时,如下操作

urlpatterns = [
    path('books/', BooksInfoAPIView.as_view({'get': 'list', 'post': 'create'}), name='all_book'),
    path('books/<pk>/', BooksInfoAPIView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}),
         name="book_detail"),
]


GenericViewSet

使用ViewSet通常并不方便,以为list、retrieve、create、update、destory等方法都需要自己编写。

GenericViewSet继承自GenericAPIViewViewSetMixin,在实现了调用as_view()时传入字典的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。

class BooksInfoAPIView(GenericViewSet):
    queryset = BooksInfo.objects.all()
    serializer_class = BookInfoModelSerializer

    def list(self, request):
        books = self.get_queryset()
        serializer = self.get_serializer(instance=books, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk):
        book = self.get_object()
        serializer = self.get_serializer(instance=book)
        return Response(serializer.data)

    def create(self, request):
        data = request.data
        serializer = self.get_serializer(data=data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
        serializer.save()
        return Response(serializer.validated_data, status=HTTP_201_CREATED)

    def update(self, request, pk):
        data = request.data
        book = self.get_object()
        serializer = self.get_serializer(instance=book, data=data, partial=True)

        if not serializer.is_valid():
            return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
        serializer.save()
        return Response(serializer.validated_data, status=HTTP_200_OK)

    def destroy(self, request, pk):
        book = self.get_object()
        book.delete()
        return Response("删除成功", status=HTTP_200_OK)

路由

urlpatterns = [
    path('books/', BooksInfoAPIView.as_view({'get': 'list', 'post': 'create'}), name='all_book'),
    path('books/<pk>/', BooksInfoAPIView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}),
         name="book_detail"),
]


GenericViewSet + Mixins

GenericViewSet还可以像GenericAPIView一样,搭配各种Mixins来实现增删改查功能

class BooksInfoAPIView(ListModelMixin, RetrieveModelMixin, CreateModelMixin, 
                       UpdateModelMixin, DestroyModelMixin, GenericViewSet):
    queryset = BooksInfo.objects.all()
    serializer_class = BookInfoModelSerializer


class HeroInfoAPIView(ListModelMixin, RetrieveModelMixin, CreateModelMixin, 
                      UpdateModelMixin, DestroyModelMixin, GenericViewSet):
    queryset = HeroInfo.objects.all()
    serializer_class = HeroInfoModelSerializer


ModelViewSet

继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

class BooksInfoAPIView(ModelViewSet):
    queryset = BooksInfo.objects.all()
    serializer_class = BookInfoModelSerializer


class HeroInfoAPIView(ModelViewSet):
    queryset = HeroInfo.objects.all()
    serializer_class = HeroInfoModelSerializer

完成最基本的增删改查功能,当需求无法完成时,可以通过重写对应方法实现功能。


ReadOnlyModelViewSet

继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin

class BooksReadOnlyView(ReadOnlyModelViewSet):
    queryset = BooksInfo.objects.all()
    serializer_class = BookInfoModelSerializer

urls:

urlpatterns = [
    path('book/', BooksReadOnlyView.as_view({'get': 'list'}), name='book'),
    path('book/<pk>/', BooksReadOnlyView.as_view({'get': 'retrieve'}), name="bookDetail"),
]


在视图中新增额外的行为

最基本的增删改查使用ModelViewSet可以省略代码,但当业务逻辑提升,默认的增删改查已经无法完成工作时,需要新增额外的行为来完成接口的编写

class BooksInfoAPIView(ModelViewSet):
    queryset = BooksInfo.objects.all()
    serializer_class = BookInfoModelSerializer

    # 查询最后一本书
    def latest(self, request, *args, **kwargs):
        book = BooksInfo.objects.latest('id')
        print(book)
        serializer = self.get_serializer(book)
        print(serializer.data)
        return Response(serializer.data)

    # 新增阅读量
    def read(self, request, pk, *args, **kwargs):
        data = request.data
        print(data)
        book = self.get_object()
        print(book)
        serializer = self.get_serializer(instance=book, data=data, partial=True)
        if not serializer.is_valid():
            return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
        serializer.save()
        return Response(serializer.data, status=HTTP_206_PARTIAL_CONTENT)

urls:

urlpatterns = [
    path('books/', BooksInfoAPIView.as_view({'get': 'list', 'post': 'create'}), name='all_book'),
    path('books/<int:pk>/', BooksInfoAPIView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}),
         name="book_detail"),
    path('books/latest/', BooksInfoAPIView.as_view({'get': 'latest'}), name="latest_book"),
    path('books/<int:pk>/read/', BooksInfoAPIView.as_view({'put': 'read'}), name="update_read"),
]


视图集中额外的方法


获取当前路由行为

在调度期间,当前action的名称可以通过.action属性获得。可以通过检查.action以根据当前action调整行为。

例如,可以将权限限制为只有admin才能访问list以外的其他action:

def get_permissions(self):
  if self.action == "list":
      permission_classes = [IsAuthenticated]
  else:
        permission_classes = [IsAdmin]
  return [permission() for permission in permission_classes]


标记额外的路由行为

如果需要路由特定方法,则可以用@detail_route@list_route装饰器进行修饰。

@list_route修饰器适用于在对象列表上操作的方法。@detail_route装饰器在其URL模式中包含pk,用于支持需要获取单个实例的方法。


action跳转

如果需要获取action的URL,请使用.reverse_action()方法。这是.reverse()的一个便捷包装,它会自动传递视图的请求对象,并将url_name.basename属性挂接。


总结

通过对各个类源码的拜读,我只想说DRF的作者真的把类继承的用法发挥到了完美。



  • 标签云

  • 支付宝扫码支持一下

  • 微信扫码支持一下



基于Nginx+Supervisord+uWSGI+Django1.11.1+Python3.6.5构建

版权所有 © 2020-2021 August Rush

京ICP备20007446号-1 & 豫公网安备 41100202000460号

网站地图 & RSS | Feed