9个视图子类
两个视图基类(是视图类):APIView,GenericAPIView
五个视图扩展类(不是视图类,需要配合使用):CreateModelMixin,ListModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
九个视图子类---》是视图类,不需要额外继承GenericAPIView,只需要继承9个中其中某个,就会有某个或某几个接口
from rest_framework.generics import
ListAPIView 查询所有
CreateAPIView 新增一个
ListCreateAPIView 查询所有,新增一个
RetrieveAPIView 查询一个
UpdateAPIView 修改一个
DestroyAPIView 删除一个
RetrieveUpdateAPIView 查询一个,修改一个
RetrieveDestroyAPIView 查询一个,删除一个
RetrieveUpdateDestroyAPIView 查询一个,修改一个,删除一个
代码展示
路由:
urlpatterns = [
path('books/', views.BookView.as_view()),
path('books/<int:pk>/', views.BookView.as_view()),
]
视图类:
class BooksView(ListCreateAPIView): # 查询一个,新增一个
queryset = Book.objects.all()
serializer_class = BookSerializer
class BookView(RetrieveUpdateDestroyAPIView): # 查询一个,修改一个,删除一个
queryset = Book.objects.all()
serializer_class = BookSerializer
总结9个视图子类:
"""
1 ListAPIView,CreateAPIView
2 ListAPIView+CreateAPIView =ListCreateAPIView 给BookView用的
3 RetrieveAPIView,DestroyAPIView,UpdateAPIView
4 RetrieveDestroyAPIView,RetrieveUpdateAPIView 发现Destroy和Update的组合没有,就是没有提供,猜测,必须先查出来,再修改或删除,所以它没有
5 RetrieveUpdateDestroyAPIView
"""
视图集
通过ModelViewSet编写5个接口
from rest_framework.viewsets import ModelViewSet
如果两个视图类写成一个路由,会有两个get,路由路径也不一样
drf写了一个类,ModelViewsSet,只要继承它,5个接口都有了,但是路由写法变了
-重点:一旦继承了ModelViewSet,路由写法变了---变成了.as_view({'get': 'list'})
-路由做映射,意思是如果get请求,访问这个地址,就执行视图类的list方法
路由:
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'})),
path('books/<int:pk>/', views.BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
]
视图:
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
通过ReadOnlyModelViewSet编写2个只读接口
from rest_framework.viewsets import ReadOnlyModelViewSet
# 路由
urlpatterns = [
path('books/', views.BookView.as_view({'get': 'list'})),
path('books/<int:pk>/', views.BookView.as_view({'get': 'retrieve'})),
]
# 视图类
class BookView(ReadOnlyModelViewSet): # 查询所有,新增一个,只有读的方法,没有写的方法
queryset = Book.objects.all()
serializer_class = BookSerializer
ViewSetMixin源码分析
研究ModelViewSet和ReadOnlyModelViewSet为什么路由写法变了?
modelViewSet=CreateModelMixin+RetrieveModelMixin+UpdateModelMixin+DestroyModelMixin+ListModelMixin+GenericViewSet(没见过)
GenericViewSet=ViewSetMixin【没见过】+GenericAPIView【见过,两个视图基类的】
导致路由写法变了的原因-----》ViewSetMixin 类-----》读--->注释中写了,以后配置路由变成了view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
# 在ViewSetMixin类中重写了as_view方法,那么我们想执行ViewSetMixin的as_view就要把这个类放在我们类的第一个继承中
if not actions:
# 判断如果没有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.action_map = actions
for method, action in actions.items():
# 这句话表示一次性取出actions的键值对,actions就是我们写的键值对形式的路由,{'get': 'list', 'post': 'create'}循环解压赋值
# 第一次循环:method:get,action:list
# 第一次循环:method:post,action:create
handler = getattr(self, action)
# 用反射的原理获取actions对应的方法名,action第一次是list去视图类中反射list方法,这个时候handler就是list方法
setattr(self, method, handler)
# 这句话反射修改方法名,将method:get请求方法换为handler:list方法名
return self.dispatch(request, *args, **kwargs)
# 在执行dispatch方法,还是APIView的方法执行handler对应的方法名,也就是list方法
return csrf_exempt(view)
# 返回的还是去掉csrf的校验装饰器,执行view函数
总结:
1.只要继承了ViewSetMixin的视图类,路由写法就编程了(重写的as_view)
2.编程需要传入字典映射方法:{'get': 'list', 'post': 'create'}
-只要传入了actions,以后访问get就是访问list,访问post,就是访问create
3.其他执行跟之前一样
4.以后视图类类中的方法名,可以任意命名,只要在路由中做好映射即可【重要】
from rest_framework.viewsets下有这几个类
GenericViewSet:ViewSetMixin+ GenericAPIView # 继承GenericAPIView想改变路由写法用这个
ModelViewSet:5个视图扩展类+ViewSetMixin+GenericAPIView
ReadOnlyModelViewSet:2个试图扩展类+ViewSetMixin+GenericAPIView 只读的两个
ViewSet:ViewSetMixin+ APIView # 继承APIView想改变路由写法用这个
ViewSetMixin:魔法,重新了as_view,只要继承他,以后路由写法变成了映射方法
"""
重点:
以后,如果继承APIView,但是想变路由写法【视图类中方法名任意命名】,要继承ViewSet
如果继承GenericAPIView,但是想变路由写法【视图类中方法名任意命名】,要继承GenericViewSet
"""
视图层大总结
1.两个视图基类
-APIView,GenericAPIView
2.5个视图扩展类,不是视图类,必须配合GenericAPIView
CreateModelMixin:新增一个
ListModelMixin:查询多个
RetrieveModelMixin:查询一个
UpdateModelMixin:修改一个
DestroyModelMixin:删除一个
3.9个视图子类,是视图类,只需要继承其中某一个即可
ListAPIView 查询所有
CreateAPIView 新增一个
ListCreateAPIView 查询所有,新增一个
RetrieveAPIView 查询一个
UpdateAPIView 修改一个
DestroyAPIView 删除一个
RetrieveUpdateAPIView 查询一个,修改一个
RetrieveDestroyAPIView 查询一个,删除一个
RetrieveUpdateDestroyAPIView 查询一个,修改一个,删除一个
4.视图集
-ModelViewSet:路由写法变了,只需要写两行,5个接口都有了
-ReadOnlyModelViewSet:路由写法变了,只需要写两行,2个只读接口都有了
-ViewSetMixin:不是视图类,魔法,重写了as_view,路由写法变了,变成映射了
views.BookView.as_view({'get': 'list', 'post': 'create'})
-ViewSet:ViewSetMixinViewSetMixin
-GenericViewSet:ViewSetMixin+ GenericAPIView
# 短信接口,视图类叫SendView,方法叫send_sms,路由配置变了
视图层:
class SendView(ViewSet):
def send_msg(self, request):
return Response('发送成功')
路由层:
urlpatterns = [
path('admin/', admin.site.urls),
path('send/', views.SendView.as_view({'get': 'send_msg'})),
]
# 这里执行的就是ViewSetMixin的as_view,将get方法转为了send_msg方法执行
路由系统
自动生成路由
drf 由于继承ViewSetMinxin类,路由写法就改变了
-原生+drf,以后的路由写法,可能会有一下情况(三种情况)
-path('books/',views.BookView.as_view())
-path('books',views.BookView.as_view({'get':'list','post':'create'}))
-自动生成
drf提供了两个具有类,继承ModelViewSet后,路由可以自动生成
from rest_framework.routers import SimpleRouter,DefaultRouter
from django.urls import path,include
使用步骤:
第一步:导入路由类
第二步:实例化得到对象(两个类,一般使用SimpleRouter)
第三步:注册:router.register('books',views.BookView,'books')
# 第一个参数是路径,不要带/
# 第二个参数是视图类
# 第三个参数是别名,一般跟路径相同
第四步:在在urlpatterns中注册,两种方式
-urlpatterns += router.urls
-include:path('/api/v1/', include(router.urls)) 此方式用的多一些
# 底层实现,自动生成路由
-本质是自动做映射,能够自动生成的前提是,视图类中要有5个方法的某个或者多个
-get---》list
-get---》retrieve
-put---》update
-post---》create
-delete---》destory
-ModelViewSet,ReadOnlyModelViewSet可以自动生成
-9个视图子类+配合ViewSetMixin 才可以自动生成
-GenericAPIView+5个视图扩展类+配合ViewSetMixin 才能自动生成
action装饰器
action 写在视图类的方法上,可以自动生成路由
使用步骤:
-写在视图类方法上
class SendView(ViewSet):
# method指定请求方法,可以传多个
# detail,只能传True或False
-False:不带id的路径:send/send_sms/
-True:带id的路径:send/2/send_sms/
# url_path,生成send后路径的名字,默认以方法名命名
# url_name,别名,反向解析使用
@action(methos=['POST'],detail=False)
def send_sms(self,request);
pass
以后看到的drf路由写法:
后期,都是自动生成,一般不在urlpatterns 加入路由了
补充:
-1 不同请求方法可以使用不同序列化类
-2 不同action使用不同序列化类
class SendView(GenericViewSet):
queryset = None
serializer_class = '序列化类'
def get_serializer(self, *args, **kwargs):
if self.action=='lqz':
return '某个序列化类'
else:
return '另一个序列化列'
@action(methods=['GET'], detail=True)
def send_sms(self, request,pk):
print(pk)
# 手机号,从哪去,假设get请求,携带了参数
phone = request.query_params.get('phone')
print('发送成功,%s' % phone)
return Response({'code': 100, 'msg': '发送成功'})
@action(methods=['GET'], detail=True)
def lqz(self,request): # get
# 序列化类
pass
@action(methods=['GET'], detail=True)
def login(self,request): # get
# 序列化类
pass