drf 视图类、视图集、路由
一、视图类
1、写 publish: 查询所有,查询单条,修改一条,新增一条,删除一条的接口
1 2 3 4 5 6 7 | class PublishView(ListCreateAPIView): queryset = Publish.objects. all () serializer_class = PublishSerializer class PublishDetailView(RetrieveUpdateDestroyAPIView): queryset = Publish.objects. all () serializer_class = PublishSerializer |
2、写 publish 查询单条,新增一条的接口--->使用9个视图子类编写
1 2 3 4 5 6 7 | class PublishView(CreateAPIView): queryset = Publish.objects. all () serializer_class = PublishSerializer class PublishDetailView(RetrieveAPIView): queryset = Publish.objects. all () serializer_class = PublishSerializer |
3、写 publish: 查询单条,新增一条,的接口--->使用5个视图扩展类+GenericAPIView
1 2 3 4 5 6 7 8 9 10 | class PublishView(GenericAPIView,CreateModelMixin): queryset = Publish.objects. all () serializer_class = PublishSerializer def post( self ,request, * args, * * kwargs): return self .create(request, * args, * * kwargs) <br> class PublishDetailView(GenericAPIView,RetrieveModelMixin): queryset = Publish.objects. all () serializer_class = PublishSerializer def get( self ,request, * args, * * kwargs): return self .retrieve(request, * args, * * kwargs) |
二、视图集
1、
1 2 3 4 5 6 7 8 9 | 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()中:写一个字典,请求方法对应接口增删改查方法
1 2 3 4 | 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---> 继承了五个扩展类 + GenericViewSet----> ViewSetMixin(决定了路由的新写法)----> as_view()(多了action)---->view(request)
1 2 3 4 5 6 | 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'}))
1 2 3 4 5 | class GenericViewSet(ViewSetMixin, generics.GenericAPIView): <br> # 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()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | - 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、视图层中类的总结
- ModelViewSet:5个接口的
- ReadOnlyModelViewSet:两个接口,list和retrieve
- ViewSetMixin:魔法,不能单独使用,必须配合视图类用,路由写法变了,映射。
- ViewSet:ViewSetMixin+APIView,以后想继承APIView,但是路由写法变化,视图类中方法可以任意命名
- GenericViewSet:ViewSetMixin+GenericAPIView,以后想继承GenericAPIView,但是路由写法变化,视图类中方法可以任意命名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | # 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、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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' ) |
路由:
1 | path( 'users/' , UserView.as_view({ 'post' : 'register' })), |
由上可知: 之前只能填写post、get、put、delete方法名的现在可以随意命名了
6、
# 以后写的接口,只想有 获取单条和获取所有,继承它
三、
1 2 3 4 5 6 7 | 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}) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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 |
1 2 3 4 | print ( self .action) # 视图类的对象中有个action属性---》它是当次请求执行的方法名的字符串 # 通过action可以限制视图类中某个方法使用的序列化类是哪个 |
案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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、之前的路由写法
1 | path( 'books/' , BookView.as_view()) |
视图集ModelViewSet 继承了ViewSetMixin (改写了as_view()),路由改变
1 | path( 'publish/' , PublishView.as_view({ 'get' : 'list' , 'post' : 'create' })) |
这样写法做映射,可能有些麻烦,于是drf,帮咱们封装了两个路由类---》可以帮助咱们快速生成之前咱们写的映射关系
1 2 3 4 5 | ## 自动生成路由:自动映射如下: { 'get' : 'list' , 'post' : 'create' } { 'get' : 'retrieve' , 'put' : 'update' , 'delete' : 'destroy' } ## 其他的-->视图类中有别的方法,我们想做映射,需要使用装饰器 |
2、使用方法
大前提:必须是继承ViewSetMixin+APIView及其子类才能自动生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | ####使用步骤 # 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、
1 2 3 4 | SimpleRouter, DefaultRouter区别 - DefaultRouter生成的路径多一个根路径 api - root - DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据 以后就用:SimpleRouter就可以 |
4、action装饰器有参装饰器的用法
使用场景:
在drf封装的路由 SimpleRouter场景中,view视图类定义了既定义了create又定义了register方法,这时候不知到哪种请求执行哪个方法。
作用:为视图类中的方法做路径的映射,这些方法要排除5个 :create,list,destroy, update、retrieve
1 2 3 4 5 6 | class UserView(GenericViewSet): def create( self , request): pass def register( self , request, pk): return Response( 'registered' ) |
使用介绍:
1 2 3 4 5 | @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/方法名/
1 2 3 4 5 6 7 8 9 10 11 12 | 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' ) |