视图集/路由自动生成/action装饰器

9个视图子类

在此文件中 from rest_framework.generics 有九个视图子类 功能分别如下

9个视图子类---视图类,不需要额外继承GenericAPIView,只需要继承9个中其中某个,就会有某个或某几个接口

CreateAPIView                      新增数据接口    post
ListAPIView                        获取全部数据    get
RetrieveAPIView                    获取单个数据    get
DestroyAPIView                     删除单个数据    delete
UpdateAPIView                      修改单个数据    put
ListCreateAPIView                  获取全部和新增数据  get post
RetrieveUpdateAPIView              查询单个和修改单个  get put
RetrieveDestroyAPIView             查询单个和删除单个  get delete
RetrieveUpdateDestroyAPIView       查询、修改、删除

## 路由
urlpatterns = [
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>/', views.BookView.as_view()),
]

# 视图类
class BookView(ListCreateAPIView):  # 查询所有,新增一个
    queryset = Book.objects.all()
    serializer_class = BookSerializer


# 新增一个,修改一个,删除一个
class BookDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

视图集

1.0版本 视图类ModelViewSet与路由层.as_view({'get': 'list'})搭配使用

from rest_framework.viewsets import ModelViewSet

class BookView(ModelViewSet):  # 继承了这个以后就相当于拥有了5个功能
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
重点,一旦继承了ModelViewSet那路由写法就要改变为
   path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'})),
   path('books/<int:pk>/', views.BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
    
 # 通过键值对来 判断 什么请求方式 执行 什么方法

ModelViewSet分析

这个类又继承了 5个视图扩展类 + GenericViewSet 这个类 
GenericViewSet 这个类 又继承了GenericAPIView ViewSetMixin
所以 ModelViewSet 可以拥有 5个功能 

但是导致我们更改url写法的是ViewSetMixin

ViewSetMixin分析

ViewSetMixin重写了as_view方法 所以 以后配置路由变为了
views.BookView.as_view({'get': 'list', 'post': 'create'})
这样当路由请求来的时候 其实是执行了ViewSetMixin的as_view方法
 @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        # 如果没有传actions,直接抛异常,路由写法变了后,as_view中不传字典,直接报错,actions就是路由里面填写的字典 
        if not actions:
            raise TypeError("The `actions` argument must be provided when "
                            "calling `.as_view()` on a ViewSet. For example "
                            "`.as_view({'get': 'list'})`")
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            self.action_map = actions
            for method, action in actions.items():
              # 循环对我们传入的字典解压赋值
              # method = get    action = list
                handler = getattr(self, action)
                # 通过反射视图类中action对应的方法
                # handler就是视图类中的list方法
                setattr(self, method, handler)
								# 反射修改,把method:get的请求方式变为了执行list方法
            return self.dispatch(request, *args, **kwargs)
        # 去除了csrf校验
        return csrf_exempt(view)

#  ViewSetMixin的as_view方法的本质是执行了 view方法
            

总结

'''
from rest_framework.viewsets下有这几个类
ModelViewSet:5个试图扩展类+ViewSetMixin+GenericAPIView
ReadOnlyModelViewSet::2个试图扩展类+ViewSetMixin+GenericAPIView   只读的两个
ViewSetMixin:魔法,重新了as_view,只要继承他,以后路由写法变成了映射方法
ViewSet:ViewSetMixin+ APIView
GenericViewSet:ViewSetMixin+ GenericAPIView
'''

只要是继承了ViewSetMixin的视图类,路由写法都变为了需要(重写了as_veiw)
actions就是我们传入的字典,通过反射来控制 访问方式执行不同的功能
以后视图类中的方法名,可以任意命名,只要在路由中做好映射即可

eg:
path('user/', views.UserView.as_view({'get': 'login', 'post': 'register'}))

class UserView(GenericViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def login(self,request):
        return Response({'code':'100','msg':'登录成功'})
    
    def register(self,request):
      	pass
# 视图类中的方法名,可以任意命名,只要在路由中做好映射即可



# 如果是跟数据库无关的功能接口 例如 短信验证码 等 尽量使用ViewSet

path('msg/<int:pk>/', views.SendView.as_view({'post': 'send_sms'}))
from rest_framework.viewsets import ViewSet

 class SendView(ViewSet):
     def send_sms(self, request,pk):
         print(pk)
         phone = request.query_params.get('phone')
         # 手机号,从哪去,假设get请求,携带了参数
         print('发送成功,%s' % phone)
         return Response({'code': 100, 'msg': '发送成功'})
        
# 以后,你想继承APIView,但是想变路由写法【视图类中方法名任意命名】,要继承ViewSet (跟数据库无关建议使用)
# 以后,你想继承GenericAPIView,但是想变路由写法【视图类中方法名任意命名】,要继承GenericViewSet (跟数据库有关建议使用)

路由系统 自动生成

# drf 由于继承ViewSetMinxin类,路由写法变了
	-原生+drf,以后的路由写法,可能会有如下情况(三种情况)
    	-path('books/', views.BookView.as_view()
      -path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'}))
       -自动生成
            

如何自动生成路由 -------------------------------------------
#需要 继承ModelViewSet后,路由可以自动生成
            
from rest_framework.routers import SimpleRouter,DefaultRouter
            
routers = SimpleRouter()
# 1.通过导入的生成路由的类产生对象
routers.register('user', views.UserView, 'user')
# 2.向对象内注册一个路由 三个参数分别为 路由的地址  路由关联的视图类 路由别名
# 会自动生成 https//127.0.0.1:8000/user/     https//127.0.0.1:8000/user/<int:pk>
            
urlpatterns = [
    path('admin/', admin.site.urls),
]

urlpatterns = urlpatterns + routers.urls
#3.将自动生成的路由添加到django路由中
            
 
SimpleRouter与DefaultRouter的区别是:DefaultRouter会多附带一个默认的api根视图 返回一个包含所有列表视图的超链接响应数据

一般都使用SimpleRouter

include写路由

from django.urls import path, include
# 导入该模块

from rest_framework.routers import SimpleRouter,DefaultRouter
            
routers = SimpleRouter()
routers.register('user', views.UserView, 'user')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include(router.urls)),]


优点:可以自定义路径前缀  建议使用


底层实现:
 -本质是自动做映射,能够自动成的前提是,视图类中要有 5个方法其中的某一个或者多个
           get--->list
           get---->retrieve
           put---->update
           post---->create
           delete---->destory
所以需要搭配视图类继承-ModelViewSet或ReadOnlyModelViewSet或
viewsetMixin+9个视图子类/ViewSetMixin+GenericAPIView+5个试图扩展类
才可以自动生成


eg: class UserView(ViewSetMixin,ListAPIView,RetrieveAPIView):
  # 该接口只能读数据
    queryset = User.objects.all()
    serializer_class = UserSerializer

action装饰器

原始方法:
from rest_framework.viewsets import ViewSet

class SendView(viewSet):
  def send_msg(self,request):
    phone = request.query_params.get('phone')
    return Response({'msg':'发送成功'})

  path('send/', view.SendView.as_view({'get':'send_msg'})),
  

from rest_framework.decorators import action

action装饰器:
  class SendView(viewSet):
  @action(methods=['GET'], detail=False)
  # 设置当使用get请求访问该路由 执行该方法
  def send_msg(self,request):
    phone = request.query_params.get('phone')
    return Response({'msg':'发送成功'})
  
路由自动生成:
routers = SimpleRouter()
routers.register('send', views.Send, 'send')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include(routers.urls)),
]
  
当访问:http://127.0.0.1:8000/api/v1/send/send_msg 即可触发该方法


总结:
1.写在视图类方法上 -----
2.配置methods=['get']指定访问方式
配置detail=False(不带id的路径)send/send_msg
配置detail=True  (id的路径)  send/2/send_msg
url_path:生成send后路径的名字,不填写默认以方法名命名 
url_name:别名,反向解析使用,了解即可


class Send(ViewSet):

    @action(methods=['GET'], detail=False)
    def a01(self, request):
        return Response({'msg': '1', })
    # http://127.0.0.1:8000/api/v1/send/a01

    @action(methods=['GET'], detail=False)
    def a02(self, request):
        return Response({'msg': '2'})
    # http://127.0.0.1:8000/api/v1/send/a02

    @action(methods=['GET'], detail=False)
    def a03(self, request):
        return Response({'msg': '3', })
    # http://127.0.0.1:8000/api/v1/send/a03

通过action来进行使用序列化类区分

伪代码展示:
通过重写get_serializer方法 来判断执行的方法是哪个,从来用出不同的序列化类

class Send(GenericViewSet):
    queryset = None
    serializer_class = UserSerializer

    def get_serializer(self,*args,**kwargs):
        if self.action == 'a01':
            return UserSerializer
        else:
            return BookSerializer

    @action(methods=['GET'], detail=False)
    def a01(self, request):
        return Response({'msg': '1', })

    @action(methods=['GET'], detail=False)
    def a02(self, request):
        return Response({'msg': '2'})

    @action(methods=['GET'], detail=False)
    def a03(self, request):
        return Response({'msg': '3', })
posted @   Python-moon  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示