drf 视图类、视图集、路由

一、视图类

1、写 publish: 查询所有,查询单条,修改一条,新增一条,删除一条的接口

class PublishView(ListCreateAPIView):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer

class PublishDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer

2、写 publish 查询单条,新增一条的接口--->使用9个视图子类编写

class PublishView(CreateAPIView):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer

class PublishDetailView(RetrieveAPIView):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer

3、写 publish: 查询单条,新增一条,的接口--->使用5个视图扩展类+GenericAPIView

class PublishView(GenericAPIView,CreateModelMixin):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer
    def post(self,request,*args,**kwargs):
        return self.create(request,*args,**kwargs)

class PublishDetailView(GenericAPIView,RetrieveModelMixin): queryset = Publish.objects.all() serializer_class = PublishSerializer def get(self,request,*args,**kwargs): return self.retrieve(request,*args,**kwargs)  

二、视图集

1、ModelViewSet

只要视图类继承了它,路由写法改一下,5个接口都有了

from rest_framework.viewsets import ModelViewSet

class PublishView(ModelViewSet):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer

class PublishDetailView(ModelViewSet):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer

2、修改路由

在路由.as_view()中:写一个字典,请求方法对应接口增删改查方法

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

3、ModelViewSet源码分析

ModelViewSet---> 继承了五个扩展类 + GenericViewSet----> ViewSetMixin(决定了路由的新写法)----> as_view()(多了action)---->view(request)

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):

问一:

  查询所有接口 -get--list----> 拿到所有数据,序列化--返回。

  新增一条 -post---create---> 之前写的新增的代码一样的

答一:

  由于继承了5个扩展类,序列化、反序列化、数据校验都有了

问二:

  为什么get 就变成了list ?为什么post 就变成了create

答二:ViewSetMixin类重新封装了as_view(),使得请求方式可以对应方法

path('publishs/', PublishView.as_view({'get': 'list', 'post': 'create'}))

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):

# ViewSetMixin必须在左侧,保障了as_view是ViewSetMixin的as_view Overrides `.as_view()` so that it takes an `actions` keyword that performs the binding of HTTP methods to actions on the Resource.

view()

-class GenericViewSet(ViewSetMixin, generics.GenericAPIView):ViewSetMixin必须放前面--》保证执行的as_view是ViewSetMixin的
        
    -请求来了,路由匹配成功---》执行ViewSetMixin的as_view内的view(request)
        def view(request, *args, **kwargs):
            self = cls(**initkwargs) # 类实例化得到对象--》self是谁的对象?PublishView
            self.action_map = actions # {'get':'list','post':'create'}
            # method:get
            # action: list
            for method, action in actions.items():
                # list 方法
                handler = getattr(self, action) #PublishView对象中反射list,拿到了
                # 反射设置值
                #setattr(PublishView视图类的对象,get,list 方法)
                # PublishView视图类的对象中就会有一个get方法,就是list
                setattr(self, method, handler)
            return self.dispatch(request, *args, **kwargs)
# 总结:
	-路由中这样配置:PublishView.as_view({'get':'list','post':'create'})
    -以后get请求过来,本质执行的就是视图类中的list方法 

4、视图层中类的总结

- ModelViewSet5个接口的

- ReadOnlyModelViewSet:两个接口,list和retrieve

- ViewSetMixin:魔法,不能单独使用,必须配合视图类用,路由写法变了,映射。

- ViewSet:ViewSetMixin+APIView,以后想继承APIView,但是路由写法变化,视图类中方法可以任意命名

- GenericViewSet:ViewSetMixin+GenericAPIView,以后想继承GenericAPIView,但是路由写法变化,视图类中方法可以任意命名

# 1 两个视图基类
	-APIView和GenericAPIView
    -APIView的执行流程:包装了新的  处理了csrfrequeset,执行了3大认证,处理全局异常
    -GenericAPIView:要做序列化,要跟数据库打交道,就直接继承它即可
    	-queryset
        -serializer_class
        
        -get_object
        -get_queryset
        -get_serializer
        
        
# 2 5个视图扩展类(不是视图类),需要GenericAPIView才能用
	-快速使用5个接口
    -某几个接口:查询单条,新增一条,的接口--->使用5个视图扩展类+GenericAPIView
    	class PublishView(GenericAPIView,CreateModelMixin)
        	queryset=Publish.objects.all()
        	serializer_class=序列化类
            def post(self,request)
            	return self.create(request)
        class PublishDetailView(GenericAPIView,RetrieveModelMixin)
        	queryset=Publish.objects.all()
        	serializer_class=序列化类
            def get(self,request)
            	return self.retrieve(request)
            
            
# 3 9个视图子类(继承GenericAPIView+5个视图扩展类的组合)
	ListAPIView, CreateAPIView  
    ListCreateAPIView
    
	RetrieveAPIView, DestroyAPIView, UpdateAPIView
	RetrieveUpdateDestroyAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView
    
    
# 4 视图集
	-ModelViewSet:
    	-ViewSetMixin+GenericAPIView+5个视图扩展类
        -GenericViewSet+5个视图扩展类
        
    -ViewSetMixin源码:路由做映射的配置,以后视图类中方法可以随便命名
    -Viewset:ViewSetMixin+APIView---》不需要要序列化,路由写法变了
    -GenericViewSet:ViewSetMixin+GenericAPIView--》需要序列化,需要用数据库,路由写法变化
    -ReadOnlyModelViewSet:list和retrieve

5、ViewSetMixin 改变路由写法的补充

以后视图类中方法名可以随意命名,只要路由做好映射

from rest_framework.viewsets import ViewSetMixin, ViewSet, GenericViewSet

## 不和数据库打交道
# class UserView(ViewSetMixin, APIView):
# class UserView(ViewSet):   # ViewSet = ViewSetMixin + APIView
#     def register(self, request):
#         return Response('registered')

## 跟数据库打交道
# class UserView(ViewSetMixin, GenericAPIView):
class UserView(GenericViewSet):  # GenericViewSet = ViewSetMixin + GenericAPIView
    def register(self, request):
        return Response('registered')

    def login(self, request):
        return Response('login')

路由:

    path('users/', UserView.as_view({'post': 'register'})),

由上可知: 之前只能填写post、get、put、delete方法名的现在可以随意命名了

6、ReadOnlyModelViewSet

# 以后写的接口,只想有 获取单条和获取所有,继承它

三、继承ModelViewSet 视图集重写方法

1、重写list方法

重写list方法的目的是为了返回自定义的返回提示语句

class PublishView(ModelViewSet):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer

    def list(self, request, *args, **kwargs):
        res = super().list(request, *args, **kwargs)
        return Response({'code': 200, 'msg': '成功!', 'result': res.data})

2、序列化使用PublishSerializer,反序列化使用 WritePublishSerializer

from rest_framework.viewsets import ModelViewSet
from .serializer import WritePublishSerializer

##  视图集
class PublishView(ModelViewSet):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer

    def get_serializer_class(self):  # 是GenericAPIView类中的方法,返回什么,以后就以哪个序列化类继续操作
        print(self.action)
        if self.request.method == 'POST':
            return WritePublishSerializer
        else:
            return self.serializer_class

    
### 重写perform_create、在保存对象前做拦截,添加日志
    def perform_create(self, serializer):
        serializer.save()


########序列化文件
class WritePublishSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publish
        # fields = '__all__'
        fields = '__all__'

    def validate_name(self, value):
        print('我走了')
        return value

3、视图类的对象中的action参数

print(self.action)  
# 视图类的对象中有个action属性---》它是当次请求执行的方法名的字符串

# 通过action可以限制视图类中某个方法使用的序列化类是哪个

案例:

from rest_framework import viewsets

class MyModelViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    def list(self, request, *args, **kwargs):
        if self.action == 'list':
            # 执行特定于列表操作的代码
            pass
        elif self.action == 'custom_action':
            # 执行特定于自定义操作的代码
            pass
        # 其他操作的代码

    def create(self, request, *args, **kwargs):
        if self.action == 'create':
            # 执行特定于创建操作的代码
            pass
        # 其他操作的代码

四、路由

1、之前的路由写法

path('books/', BookView.as_view())

视图集ModelViewSet 继承了ViewSetMixin (改写了as_view()),路由改变

path('publish/', PublishView.as_view({'get': 'list', 'post': 'create'}))

这样写法做映射,可能有些麻烦,于是drf,帮咱们封装了两个路由类---》可以帮助咱们快速生成之前咱们写的映射关系

## 自动生成路由:自动映射如下:
{'get': 'list', 'post': 'create'}
{'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}

## 其他的-->视图类中有别的方法,我们想做映射,需要使用装饰器 

2、使用方法

大前提:必须是继承ViewSetMixin+APIView及其子类才能自动生成

####使用步骤
# urls.py中

#### 1 导入路由类
from rest_framework.routers import SimpleRouter, DefaultRouter

#### 2 类实例化得到对象
router = SimpleRouter()

#### 3 自动生成路由,调用对象的某个方法,完成跟视图类的对应关系,映射路由
router.register('publish', PublishView, 'publish')  # 三个参数:路径、视图类名、别名

# router.register('books', BookView, 'books') #  后期可以注册更多
router.register('user',UserView,'user')

##### 4 把自动生成的路由,加到总路由中
urlpatterns = urlpatterns + router.urls  # 两个列表直接相加

### 第四步可以这样写
	path('api/v1/', include(router.urls)),   # http://127.0.0.1:8008/api/v1/user/register/--->post

补充:

路由自动注册会自动加/,

router.register('banner/', views.BannerView, 'banner')  

如上写法会导致多一个/

3、SimpleRouter, DefaultRouter

SimpleRouter, DefaultRouter区别
	-DefaultRouter生成的路径多一个根路径 api-root
    -DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据
以后就用:SimpleRouter就可以

4、action装饰器有参装饰器的用法

使用场景:

在drf封装的路由 SimpleRouter场景中,view视图类定义了既定义了create又定义了register方法,这时候不知到哪种请求执行哪个方法。

作用:为视图类中的方法做路径的映射,这些方法要排除5个 :create,list,destroy, update、retrieve

class UserView(GenericViewSet):  
    def create(self, request):
         pass

    def register(self, request, pk):
        return Response('registered')

使用介绍:

    @action(methods=['POST'],detail=False)
    def register(self, request):
        return Response('register')
# 自动生成:
	http://127.0.0.1:8008/user/register/---->post--->就会执行register

action参数

   -methods:请求方式,可以写多个

   -detail:路径中是否带id号 ,detail=False:http://127.0.0.1:8000/user/register/    detail=True:http://127.0.0.1:8000/user/4/register/ 

       -url_path='xxx',指定路径的时候,路径为 http://127.0.0.1:8000/user/xxx/,不指定时候:路径为 http://127.0.0.1:8000/user/方法名/

from rest_framework.decorators import action  # 导入有参装饰器

class UserView(GenericViewSet):  # GenericViewSet = ViewSetMixin + GenericAPIView
    # 127.0.0.1:8000/user/ post 请求的默认路径就是create
    def create(self, request):
        return Response('create')

    # @action(methods=['POST'], detail=False, url_path='register')
    @action(methods=['POST'], detail=True)
    def register(self, request, pk):
        print(pk)
        return Response('registered')

  

 

posted @ 2023-09-05 17:23  凡人半睁眼  阅读(30)  评论(0编辑  收藏  举报