drf(九)—视图

drf(九)—视图

说明:drf 的开发一般使用 CBV 进行开发,Django 的原生视图类是 View 类,drf 中使用较普通的是APIView

1.GenericAPIView

该类继承于APIView,只是封装了一些调用方法,完成我们的常用操作。

使用方式

class View1View(GenericAPIView):
    queryset = models.Role.objects
    serializer_class = RoleSerializers
    pagination_class = PageNumberPagination

    def get(self, request, *args, **kwargs):
        roles=self.get_queryset() # 获取到数据库中的数据
        pager_roles=self.paginate_queryset(roles)
        ser=self.get_serializer(instance=pager_roles,many=True)
        return Response(ser.data)

使用 Pycharm 查看类的继承关系

image-20220411090234548

说明:本类使用较少不做过多解释。

2.GenericViewSet

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    """
    The GenericViewSet class does not provide any actions by default,
    but does include the base set of generic view behavior, such as
    the `get_object` and `get_queryset` methods.
    """
    pass

ViewSetMixin 优先级高于后面的 GenericAPIView 类,多继承的顺序问题。

因此我们也可以在自定义的视图中直接继承这两个类。

image-20220411095442110

class View2View(GenericViewSet):

    def get(self,rewuest,*args,**kwargs):
        return Response("....")

image-20220411100633111

函数as_view()报错,缺少参数,进行修改;

此时,视图的不同已经开始影响了路由系统。

re_path('^api/(?P<version>[v1|v2]+)/viewv2/',view.View2View.as_view({'get':'list'})),
class View2View(GenericViewSet):
    def list(self,rewuest,*args,**kwargs):
        return Response("....")

image-20220411100935373

修改后并将该方法的名称映射为字典的值。

进行源码剖析查看为何要这样传参。

class ViewSetMixin:
    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        cls.name = None
        cls.description = None

        cls.suffix = None

        cls.detail = None

        cls.basename = None

        if not actions:
            raise TypeError("The `actions` argument must be provided when "
                            "calling `.as_view()` on a ViewSet. For example "
                            "`.as_view({'get': 'list'})`")

        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            # 使用反射查看是否存在键,不存在则抛出异常
            if not hasattr(cls, key): 
                raise TypeError("%s() received an invalid keyword %r" % (
                    cls.__name__, key))
        if 'name' in initkwargs and 'suffix' in initkwargs:
            raise TypeError("%s() received both `name` and `suffix`, which are "
                            "mutually exclusive arguments." % (cls.__name__))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if 'get' in actions and 'head' not in actions:
                actions['head'] = actions['get']
            self.action_map = actions
            for method, action in actions.items():
                handler = getattr(self, action)
                # 使用反射进行方法。
                setattr(self, method, handler)
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)

        update_wrapper(view, cls, updated=())

        update_wrapper(view, cls.dispatch, assigned=())

        view.cls = cls
        view.initkwargs = initkwargs
        view.actions = actions
        return csrf_exempt(view) # 使得函数通过CSRF_TOKEN

3.ModelViewSet

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    
    # 本类继承更多的类。 
    pass

列表类

class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
# 改类帮我们写了list方法,我们不需要在进行编写

其他的类相同。

class View3View(ListModelMixin,GenericViewSet):# 只有查的功能
    queryset = models.Role.objects.all()
    serializer_class = RoleSerializers
    pagination_class = PageNumberPagination

image-20220411134322275

因此只要继承 ModelViewSet 可以直接获得增删改查的方法。

传入需要更新的视图或者查询单条记录的时候使用路由进行传参

image-20220411140121249

总结

  1. 继承的越多,自己写的代码就越少;

  2. 影响路由系统,一个 url 会写两个路由;

  3. 使用建议

    1. 当对某张表需要具备增删改查的所有操作时使用 ModelViewSet,较好

    2. 当只实现某项功能的时候可以去单独执行某项 XXXModelMixin;

      总结:
      	a.增删改查   ModelViewSet
      	b.增删      CreateModelMixin,DestroyModelMixin  GenericViewSet
      	c.复杂逻辑   GenericViewSet 或 APIView 
      
    3. 当业务功能较为复杂的时候使用APIView

补充:

视图的完整继承类图

viewsets

posted @ 2022-04-11 14:12  紫青宝剑  阅读(157)  评论(0编辑  收藏  举报