1 9个视图子类
9个视图子类就是GenericAPIView
和5个视图拓展类的组合
通过from rest_framework.generics import
导入,写视图类就只用继承视图子类
ListAPIView
,只用于展示所有CreateAPIView
,添加一条ListCreateAPIView
,展示所有和添加一条RetrieveAPIView
,展示一条DestroyAPIView
,删除一条UpdateAPIView
,修改一条RetrieveUpdateDestroyAPIView
,展示、修改、删除一条RetrieveDestroyAPIView
,展示一条,删除一条RetrieveUpdateAPIView
,展示一条,修改一条
以后想写5个接口中的某一个或某几个或所有,只需要选择继承不同的类即可,类中只需要配置两个类属性
1.1 视图类
想写 publish: 查询所有,查询单条,修改一条,新增一条,删除一条的接口
class PublishView(ListCreateAPIView):
queryset = Publish.objects.all()
serializer_class = PublishSerializer
class PublishDetailView(RetrieveUpdateDestroyAPIView):
queryset = Publish.objects.all()
serializer_class = PublishSerializer
1.2 路由
路由没变
path('publish/', PublishView.as_view()),
path('publish/<int:pk>', PublishView.as_view()),
2 视图集
将使用同一个库和序列化类的视图整合起来
2.1 ModelViewSet
视图函数
from rest_framework.viewsets import ModelViewSet
class PublishView(ModelViewSet):
queryset = Publish.objects.all()
serializer_class = PublishSerialize
路由
path('publish/', PublishView.as_view({'get':'list','post':'create'})),
path('publish/<int:pk>', PublishView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
2.1.1 ModelViewSet源码分析
-
有查询所有接口
get–list----》拿到所有数据,序列化–返回 -
新增一条
post—create—》之前咱们写的新增的代码一样的
2.2 ViewSetMixin
ModelViewSet中继承了五个视图拓展类和GenericViewSet,GenericViewSe继承了ViewSetMixin
,它重写了as_view()
ViewSetMixin
决定了,以后路由写法就变了
源码分析
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方法
2.2.1 在视图类写自定义方法
以后视图类中方法名可以随意命名,只要路由做好映射
from rest_framework.viewsets import ViewSetMixin,ViewSet
继承的类是:只要继承ViewSetMixin ,就能视图类中方法任意命名,路由写法变化
ViewSetMixin
+APIView
=ViewSet
没有表相关直接使用ViewSet
即可GenericViewSet
+ViewSetMixin
=ModelViewSet
有与表相关则用ModelViewSet
视图类
class UserView(ModelViewSet):
queryset = UserInfo.objects.all()
serializer_class = UserSerializer
def book(self, request):
return Response('from book')
路由
path('book/', UserView.as_view({'post': 'login'}))
访问地址
2.3 ReadOnlyModelViewSet
from rest_framework.viewsets import ReadOnlyModelViewSet
以后写的接口,只想有 获取单条和获取所有,继承它
2.4 视图层中类的总结
1 两个视图基类
APIView和GenericAPIView
APIView
的执行流程:
包装了新的 处理了csrfrequeset,执行了3大认证,处理全局异常GenericAPIView
要做序列化,要跟数据库打交道,就直接继承它即可- 类属性
queryset
、serializer_class
- 方法
get_object
、get_queryset
、get_serializer
- 类属性
2 5个视图扩展类(不是视图类)
需要GenericAPIView才能用
RetrieveModelMixin
查询一条,写了一个方法retrieve
,代码就是 跟咱们之前写获取单条get方法内容一样CreateModelMixin
新增一条写了一个方法create
,代码就是 跟咱们之前写新增一条 post 方法内容一样DestroyModelMixin
删除一条写了一个方法destroy
,代码就是 跟咱们之前写删除一条 delete 方法内容一样ListModelMixin
查询所有写了一个方法list
,代码就是 跟咱们之前写查询所有 get 方法内容一样UpdateModelMixin
修改一个写了一个方法update
,代码就是 跟咱们之前写修改一条put 方法内容一样
3 9个视图子类
继承GenericAPIView+5个视图扩展类的组合
参考1
视图集
-
ModelViewSet:
ViewSetMixin+GenericAPIView+5个视图扩展类
GenericViewSet+5个视图扩展类 -
ViewSetMixin源码:路由做映射的配置,以后视图类中方法可以随便命名
Viewset:ViewSetMixin+APIView—》不需要要序列化,路由写法变了
GenericViewSet:ViewSetMixin+GenericAPIView–》需要序列化,需要用数据库,路由写法变化
ReadOnlyModelViewSet:list和retrieve
3 路由
之前的路由写起来,做映射,可能有些麻烦,于是drf,帮咱们封装了两个路由类—》可以帮助咱们快速生成之前咱们写的映射关系
条件:必须是继承ViewSetMixin+APIView及其子类才能自动生成
自动生成路由:自动映射如下:
{‘get’: ‘list’, ‘post’: ‘create’}
{‘get’: ‘retrieve’, ‘put’: ‘update’, ‘delete’: ‘destroy’}
3.1 使用方法
大前提:视图类必须是继承ViewSetMixin
+APIView
及其子类才能自动生成
使用步骤
#### 1 导入路由类
from rest_framework.routers import SimpleRouter, DefaultRouter
#### 2 类实例化得到对象
router = SimpleRouter()
#### 3 自动生成路由,调用对象的某个方法,完成跟视图类的对应关系,映射路由
router.register('publish', PublishView, 'publish')
router.register('user',UserView,'user')
##### 4 把自动生成的路由,加到总路由中
urlpatterns = urlpatterns + router.urls # 两个列表直接相加
### 第四步可以这样写,在urlpatterns 中
# 给路由加前缀
path('api/v1/', include(router.urls)),
# http://127.0.0.1:8008/api/v1/user/register/--->post
3.2 SimpleRouter, DefaultRouter
SimpleRouter, DefaultRouter区别
DefaultRouter
生成的路径多一个根路径 api-rootDefaultRouter
会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据
以后就用:SimpleRouter就可以
3.3 action装饰器
在视图类写自定义方法,大致实现如2.2.1
路由不用变,在视图类中定义并使用action装饰器
使用方法
from rest_framework.decorators import action
class UserView(ModelViewSet):
queryset = UserInfo.objects.all()
serializer_class = UserSerializer
@action(methods=['POST'], detail=False)
def register(self, request):
back_dic = {'code': 200, 'msg': "注册成功"}
ser = self.get_serializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(back_dic)
else:
return Response({'code': 200, 'msg': ser.errors})
访问地址
通过特定的访问地址和post
方法即可使用register方法,
其中user是使用这个类的地址
action参数
methods
:请求方式,可以写多个detail
:路径中是否带id号
http://127.0.0.1:8008/user/register/
detail=False
http://127.0.0.1:8008/user/4/register/
detail=True
3.4 重写方法
以后继承ModelViewSet也可也能会重写好多方法
重写list
class UserView(ModelViewSet):
queryset = UserInfo.objects.all()
serializer_class = UserSerializer
# 以后可能会重写list,做自己的定制
def list(self, request, *args, **kwargs):
res = super().list(request, *args, **kwargs)
return Response({'code': 200, 'msg': '查看所有用户成功', 'result': res.data})
重写get_serializer_class
是GenericAPIView类中的方法,返回什么,以后就以哪个序列化类继续操作
class UserView(ModelViewSet):
queryset = UserInfo.objects.all()
serializer_class = UserSerializer
def get_serializer_class(self, *args, **kwargs):
if self.action == 'login':
return LoginSerializer
else:
return self.serializer_class
@action(methods=['POST'], detail=False)
def login(self, request):
back_dic = {'code': 200, 'msg': "登录成功"}
# 使用序列化类
ser = self.get_serializer(data=request.data)
if ser.is_valid():
for i in self.get_queryset():
if i.username == ser.data.get('username') and i.password == ser.data.get('password'):
return Response(back_dic)
back_dic['code'] = 1002
back_dic['msg'] = "用户密码错误"
return Response(back_dic)
else:
return Response({'code': 200, 'msg': ser.errors})
重写perform_create
数据保存前,做一些操作,比如写日志
def perform_create(self, serializer):
serializer.save()
3.4 视图类的对象中的action参数print(self.action)
视图类的对象中有个action属性—》它是单次请求执行的方法名的字符串
通过action可以限制视图类中某个方法使用的序列化类是哪个