drf-视图组件

一 视图之两个视图基类

两个视图基类有:

# 视图类:
    -APIView:之前用过
    -GenericAPIView:GenericAPIView继承了APIView

1.1 APIView

rest_framework.views.APIView

APIView是REST framework提供的所有视图的基类,继承自Django的View父类。

APIView与View的不同之处在于:

  • 去除了csrf的校验
  • 包装了新的request
  • 在执行视图类的方法之前,执行了三大认证(认证,权限,频率)
  • 处理了全局异常

支持定义的类属性

  • authentication_classes 列表或元祖,身份认证类
  • permissoin_classes 列表或元祖,权限检查类
  • throttle_classes 列表或元祖,流量控制类

在APIView中仍以常规的类视图定义方法来实现get() 、post() 、put() 、 get() 、 delete()。

使用APIView+序列化类+Response写接口

from rest_framework.views import APIView
from .serializer import BookSerialzier
from rest_framework.response import Response
from .models import Book

class BookView(APIView):
    def get(self, request):
        qs = Book.objects.all()
        ser = BookSerialzier(qs, many=True)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})

    def post(self, request):
        ser = BookSerialzier(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})


class BookDetailView(APIView):
    def get(self, request, pk):
        book = Book.objects.all().get(pk=pk)
        ser = BookSerialzier(book)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})

    def put(self, request, pk):
        book = Book.objects.get(pk=pk)
        ser = BookSerialzier(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '更新成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})

1.2 GenericAPIView[通用视图类]

rest_framework.generics.GenericAPIView

继承自APIVIew,主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。通常在使用时,可搭配一个或多个Mixin扩展类。

1.2.1 主要的属性或方法

以后,如果涉及到数据库操作,尽量用GenericAPIView。

# GenericAPIView
    -类属性:
        queryset:要序列化的所有数据
        serializer_class:序列化类
        lookup_field = 'pk' :获取单条数据的查询条件
        filter_backends:过滤类
        pagination_class:分页类,任意一个查询所有的条件都要带分页类,一条一条查询。

    -方法:
        -get_queryset:获取所有要序列化的数据【后期可以重写】
        -get_object :根据pk,获取单个对象,没有返回404。路由必须:path('/<int:pk>/', PublishDetailView.as_view()),

        -get_serializer  : 真正的实例化得到序列化类【后期可以重写,指定要序列化的类是哪一个】
        -get_serializer_class:仅仅是返回序列化类,不能传任何参数


拓展:
get_queryset():其实就是把queryset返回出去了。但为什么要写这个方法呢?
# 写成方法的目的:增加扩展性。
可以在自己的类中重写这个方法。可以根据某个条件返回不同的值,这样能够序列化的数据就变多了,是动态的,而不是写死的。
eg:
    def get_queryset(self):
        if 某种条件:
            return self.queryset
        elif 另一个条件:
            return Book.objects.all()     

1.2.2 推导GenericAPIView的封装

# 咱们写的,伪代码
# 定义一个GenericAPIView类继承APIView
class GenericAPIView(APIView):
    query_set = None
    serialzier_class = None
    
    def get_queryset(self):
        return self.query_set
    
    def get_serializer(self):
        return self.serialzier_class
    
    def get_object(self):
        return self.query_set.filter(pk=pk)


class BookView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier
    
    def get(self, request):
        qs = self.get_queryset()
        ser = self.get_serializer(qs, many=True)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})

    def post(self, request):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})


class BookDetailView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier

    def get(self, request, pk):
        book = self.get_object()
        ser = self.get_serializer(book)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})


    def put(self, request, pk):
        book = self.get_object()
        ser = self.get_serializer(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '更新成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})

1.2.3 写5个接口

使用GenericAPIView+序列化类+Response写5个接口

 # 总结:以后继承GenericAPIView写接口
    1 必须配置类属性
    	queryset
        serializer_class
     2 想获取要序列化的所有数据
    	get_queryset()
     3 想使用序列化类:
    	get_serializer
     4 想拿单条
    	get_object

代码:

from rest_framework.generics import GenericAPIView


class BookView(GenericAPIView):
    # queryset = Book.objects  # 可以不用加all(),源码中有,但最好自己加上
    queryset = Book.objects.all()  ## 
    serializer_class = BookSerialzier  ## 
    # lookup_field = 'id'

    def get(self, request):
        # qs = self.queryset  # 这个函数中也可以这么写
        qs = self.get_queryset()  ##
        ser = self.get_serializer(qs, many=True)  ##
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})

    def post(self, request):
        ser = self.get_serializer(data=request.data)  ## 
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})


class BookDetailView(GenericAPIView):
    queryset = Book.objects.all()  ## 
    serializer_class = BookSerialzier  ##  

    def get(self, request, pk):
        book = self.get_object()  ## 
        ser = self.get_serializer(book)  ## 
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})

    def put(self, request, pk):
        book = self.get_object()  ## 
        ser = self.get_serializer(data=request.data, instance=book)  ## 
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '更新成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})
   
     
# 再写publish有关的,就可以复制上面的,改动两个类的名字,改动两个类属性就行
    queryset = Publish.objects.all()  
    serializer_class = PublishSerialzier  

"""如果涉及到数据库操作,尽量用GenericAPIView"""

二 5个视图扩展类

2.1 方法

5个类(不叫视图类,视图扩展类,需要配合GenericAPIView一起用),每个类有一个方法,以后想写哪个接口,就继承哪个类即可。

from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin
'''
    -查询所有 :ListModelMixin ---> list方法
    -新增一条:CreateModelMixin ---> create方法
    
    -查询单条:RetrieveModelMixin ---> retrieve方法
    -修改一条:UpdateModelMixin ---> update方法
    -删除一条:DestroyModelMixin ---> destroy方法
'''

2.2 自己封装

# 不需要继承类

class ListModelMixin:  # 查询所有
    def list(self, request, *args, **kwargs):
        qs = self.get_queryset()
        ser = self.get_serializer(qs, many=True)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})


class CreateModelMixin:  # 新增数据
    def create(self, request, *args, **kwargs):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})


class RetrieveModelMixin:  # 查询单条
    def retrieve(self, request, *args, **kwargs):
        book = self.get_object()
        ser = self.get_serializer(book)
        return Response({'code': 100, 'msg': '成功', 'results': ser.data})


class UpdateModelMixin:  # 修改一条
    def update(self, request, *args, **kwargs):
        book = self.get_object()
        ser = self.get_serializer(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '更新成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})


class DestroyModelMixin:  # 删除一条
    def destroy(self, request, *args, **kwargs):
        self.get_object().delete()
        return Response({'code': 100, 'msg': '删除成功'})

2.3 源码

'''
    -查询所有 :ListModelMixin ---> list方法
    -新增一条:CreateModelMixin ---> create方法
    
    -查询单条:RetrieveModelMixin ---> retrieve方法
    -修改一条:UpdateModelMixin ---> update方法
    -删除一条:DestroyModelMixin ---> destroy方法
'''

ListModelMixin的源码:

class ListModelMixin:
    """
    List a queryset.查询所有
    快速实现列表视图,返回200状态码。
    """
    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)

CreateModelMixin的源码:

class CreateModelMixin:
    """
    新增一条
    Create a model instance.
    成功返回201状态码。

如果序列化器对前端发送的数据验证失败,返回400错误。
    """
    def create(self, request, *args, **kwargs):
        # 获取序列化器
        serializer = self.get_serializer(data=request.data)
        # 验证,不正确主动抛异常
        serializer.is_valid(raise_exception=True)
        # 保存,   也可以自己写perform_create方法,提高了扩展性
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

RetrieveModelMixin的源码:

class RetrieveModelMixin:
    """
    查询一条
    Retrieve a model instance.
    如果存在,返回200, 否则返回404。
    """
    def retrieve(self, request, *args, **kwargs):
        # 获取对象,会检查对象的权限
        instance = self.get_object()
        # 序列化
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

UpdateModelMixin的源码:

class UpdateModelMixin:
    """
    修改一条
    Update a model instance.
    成功返回200,序列化器校验数据失败时,返回400错误
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        # 序列化
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        # 验证,不正确主动抛异常
        serializer.is_valid(raise_exception=True)
        # 保存数据
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

DestroyModelMixin的源码:

class DestroyModelMixin:
    """
    删除一条
    Destroy a model instance.
    成功返回204,不存在返回404。
    """
    def destroy(self, request, *args, **kwargs):
        # 获取对象,会检查对象的权限
        instance = self.get_object()
        # 删除数据
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

2.4 具体使用,写5个接口

# 5 个视图扩展类--->不是视图类---> 必须配合GenericAPIView及其子类使用---> 不能配合APIView使用
# 5个视图扩展类,每一个类中只有一个方法,完成5个接口中的其中一个,想写多个接口,就要继承多个。这种思维方法叫mixin,混入


# 3 继承GenericAPIView+5个视图扩展类+序列化类+Response写接口
from .models import Book
from .serializer import BookSerializer
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin

class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)



class BookDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

三 9个视图子类

3.1 介绍

# 9个视图子类--->视图类
from rest_framework.generics import ListAPIView, CreateAPIView, UpdateAPIView, DestroyAPIView, RetrieveAPIView
from rest_framework.generics import ListCreateAPIView
from rest_framework.generics import RetrieveUpdateDestroyAPIView
from rest_framework.generics import RetrieveUpdateAPIView
from rest_framework.generics import RetrieveDestroyAPIView
# 没有UpdateDestroyAPIView,写接口是为了配合前端使用,前端只有查到这个数据才能够修改,如果都不能查到,就不会有修改和删除方法了。

1)CreateAPIView

提供 post 方法

继承自: GenericAPIView、CreateModelMixin

2)ListAPIView

提供 get 方法

继承自:GenericAPIView、ListModelMixin

3)RetrieveAPIView

提供 get 方法

继承自: GenericAPIView、RetrieveModelMixin

4)DestoryAPIView

提供 delete 方法

继承自:GenericAPIView、DestoryModelMixin

5)UpdateAPIView

提供 put 和 patch 方法

继承自:GenericAPIView、UpdateModelMixin

6)RetrieveUpdateAPIView

提供 get、put、patch方法

继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin

7)RetrieveUpdateDestoryAPIView

提供 get、put、patch、delete方法

继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin

3.2 具体使用

# 4 基于视图子类写5 个接口:9个视图子类--->视图类
from .models import Book
from .serializer import BookSerializer
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView

class BookView(ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier


class BookDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier

四 视图集

4.1 ModelViewSet写5个接口

总结

# ModelViewSet:

    -继承它后,只需要在视图类中写两行
    	queryset = Book.objects.all()
    	serializer_class = BookSerialzier

    -配置路由,5个接口都有了
    	path('books/', BookView.as_view({'get': 'list', 'post': 'create'})),
    	path('books/<int:pk>/', BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),       

视图函数

### 5 视图集 ---> 写5个接口
from .models import Book
from .serializer import BookSerializer
from rest_framework.viewsets import ModelViewSet


class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerialzier

路由

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

4.3 ModelViewSet 源码分析

# ModelViewSet是个视图类,必须继承GenericAPIView或子类
# ModelViewSet 源码分析
	-继承了:      
    	     mixins.CreateModelMixin,
             mixins.RetrieveModelMixin,
             mixins.UpdateModelMixin,
             mixins.DestroyModelMixin,
             mixins.ListModelMixin  # 5个视图扩展类
             GenericViewSet
                -ViewSetMixin :没有见过, 重写了 as_view
                -GenericAPIView
                
                
     -只要继承了ModelViewSet,路由写法变成映射的关系了,谁控制它变的:ViewSetMixin
   

# ViewSetMixin 如何控制路由写法变了?
	-BookView.as_view 是在执行,其实是ViewSetMixin的as_view
        @classonlymethod
        def as_view(cls, actions=None, **initkwargs):
            #我们传入的 actions={'get': 'list', 'post': 'create'}
            def view(request, *args, **kwargs):
                self = cls(**initkwargs)
                for method, action in actions.items():
                    # method:get
                    # action:list
                    # 通过反射,self是BookView的对象,取BookView对象中反射list
                    handler = getattr(self, action)
                    # 通过反射:setattr(BookView的对象,'get',list方法)
                    setattr(self, method, handler)
                # APIViwe的dispatch
                return self.dispatch(request, *args, **kwargs)
            return csrf_exempt(view)
	
    
    
# 总结:
'''
    1 只要继承了ViewSetMixin及其子类,路由写法就变了,必须传actions参数,没有传入action参数,会报错,主动抛异常
    2 变成映射关系了:
        path('books/', BookView.as_view({'get': 'list', 'post': 'create'})),
    3 以后,只要是books路由匹配成功,的get请求,就会执行视图类BookView的list方法
    
    4 以后视图类中的方法名,可以随意命名
    5 ViewSetMixin这个类,必须配合视图类使用(APIView,GenericAPIView,9个视图子类),必须放在视图类之前(as_view()要先执行)
'''

4.4 视图集中的所有类

from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSetMixin,ViewSet,GenericViewSet


ViewSetMixin:魔法,不能单独使用,必须配合视图类用,重写了as_view,路由写法变了
ViewSet =ViewSetMixin+APIView
GenericViewSet = ViewSetMixin+GenericAPIView
ModelViewSet:继承了5个视图扩展类和GenericViewSet
ReadOnlyModelViewSet:只读视图类 ---> 只有两个,继承查询所有和查询单条+GenericViewSet

0) ViewSetMixin

只要继承了ViewSetMixin及其子类,路由写法就变了,变成映射关系。必须传actions参数,没有传入action参数,会报错,主动抛异常。

但它不能单独使用,必须配合视图类用

path('books/', BookView.as_view({'get': 'list', 'post': 'create'})),

1) ViewSet

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

ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典(如{‘get’:’list’})的映射处理工作。

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

2)GenericViewSet

使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView。

GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView与ViewSetMixin,在实现了调用as_view()时传入字典(如{'get':'list'})的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。

3)ModelViewSet

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

4)ReadOnlyModelViewSet

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

4.5 视图中自定义的方法名

视图函数

from rest_framework.viewsets import ViewSetMixin
from rest_framework.views import APIView

class SmsView(ViewSetMixin, APIView):
    # 视图类中的方法可以随意命名
    def lqz(self, request):
        return Response('你好')

路由

path('lqz/', SmsView.as_view({'get':'lqz'})),

# http://127.0.0.1:8000/lqz/地址的get请求,就会执行SmsView视图类中自定义的lqz方法。

4.5 ModelViewSet的拓展

4.5.1 重写perform_create

  • 继承了ModelViewSet,每增加一条记录,就干某个事,重写perform_create
# 具体使用时,就可以自己重写内部的方法,扩展性很高
from rest_framework.viewsets import ModelViewSet


class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
	
    # 自己重写list方法
    def list(self, request, *args, **kwargs):
        # 继承父类的list方法,也就是真正的list方法
        res = super().list(request, *args, **kwargs)
        return Response({'code': 100, 'msg': '成功', 'data': res.data})
	
    # 重写create方法
    def create(self, request, *args, **kwargs):
        # 真正的新增数据方法
        res = super().create(request, *args, **kwargs)
        print('发送邮件。。。')
        return Response({'code': 100, 'msg': '新增成功'})
    
    # # 或者也可以重写perform_create方法,两个重写一个就行
    # def perform_create(self, serializer):
    #     # 在新增之前,加入逻辑
    #     print('发送邮件。。。。')
    #     super(BookView, self).perform_create(serializer)

4.5.2 重写序列化类--get_serializer_class

  • 序列化使用一个序列化类,反序列化使用配置的那个序列化类
class BookView(ModelViewSet):
    queryset = None
    serializer_class = None
    
    def get_serializer_class(self):
        # 判断,请求是get,还是其它
        # self中保存有请求对象
        if self.request.method=='get':
            return 某个序列化类
        else:
            return self.serializer_class

4.5.3 动态变序列化类和动态获取不同对象

  • 自定义了一个方法:login,使用了action装饰器,让它取到的qs对象和序列化类跟配置的都不一样
##### 视图类 :self  内部有request对象,还有个action,就是视图类中方法的字符串名


class BookView(ModelViewSet):
    queryset = None
    serializer_class = None
    
    # 动态变序列化类
    def get_serializer_class(self):
        # self的action就是路径的名字
        if self.action =='login':
            return '某个序列化类'
        elif self.action=='create':
            return '另一个序列化类'
        else:
            return super().get_serializer_class()
        
    def get_queryset(self):
        # self.action=='login':
        if 'login' in self.request.path:
            return 单独的qs
        else:
            return super().get_queryset()

    def create(self, request, *args, **kwargs):
        self.get_serializer()

    # /books/login/
    # /books/   ---->post--->create   self.get_serializer
    @action(methods=['POST'], detail=False)
    def login(self, request):
        self.get_queryset()  # 获取所有数据
        self.get_serializer()  # 获取序列化类
    

六 总结

# 两个视图基类
    -APIView
    -GenericAPIView:有关数据库操作,queryset 和serializer_class


# 5 个视图扩展类(rest_framework.mixins)
不是视图类,需要配合GenericAPIView及其子类使用
    -CreateModelMixin:create方法创建一条
    -DestroyModelMixin:destory方法删除一条
    -ListModelMixin:list方法获取所有
    -RetrieveModelMixin:retrieve获取一条
    -UpdateModelMixin:update修改一条
	

# 9个视图子类(rest_framework.generics)
    -CreateAPIView:有post方法,新增数据
    -DestroyAPIView:有delete方法,删除数据
    -ListAPIView:有get方法获取所有
    -UpdateAPIView:有put方法,修改数据
    -RetrieveAPIView:有get方法,获取一条

    -ListCreateAPIView:有get获取所有,post方法新增
    -RetrieveDestroyAPIView:有get方法获取一条,delete方法删除
    -RetrieveUpdateAPIView:有get获取一条,put修改
    -RetrieveUpdateDestroyAPIView:有get获取一条,put修改,delete删除


# 视图集:
    -ModelViewSet:5个接口的
    -ReadOnlyModelViewSet:两个接口,list和retrieve
    -ViewSetMixin:魔法,不能单独使用,必须配合视图类用,路由写法变了,映射。
    -ViewSet:ViewSetMixin+APIView,以后想继承APIView,但是路由写法变化,视图类中方法可以任意命名
    -GenericViewSet:ViewSetMixin+GenericAPIView,以后想继承GenericAPIView,但是路由写法变化,视图类中方法可以任意命名
posted @ 2023-05-24 22:33  星空看海  阅读(30)  评论(0编辑  收藏  举报