9个视图子类、视图集、路由系统
9个视图子类
2个视图基类
APIView
GenericAPIView
5个视图扩展类
DestroyModelMixin
RetrieveModelMixin
ListModelMixin
CreateModelMixin
UpdateModelMixin
9个视图子类 视图类 不需要额外继承不需要额外继承GenericAPIView 只需要继承9个中其中某个 就会有某个或某几个接口
DestroyAPIView
RetrieveAPIView
RetrieveDestroyAPIView
RetrieveUpdateAPIView
RetrieveUpdateDestroyAPIView
ListAPIView
ListCreateAPIView
CreateAPIView
UpdateAPIView
第四层:高级
views.py
class BooksViews(ListCreateAPIView):
queryset= Books
serializer_class=BookSerializers
class BookViews(RetrieveUpdateDestroyAPIView):
queryset= Books
serializer_class=BookSerializers
ListCreateAPIView 里面写好了 get 和post 不需要单独在写
同样 RetrieveUpdateDestroyAPIView里面也封装了一些方法
视图集
1.通过ModelViewSet编写5个接口
from rest_framework.viewsets import ModelViewSet
第五层:高高级
url.py
urlpatterns = [
path('admin/', admin.site.urls),
path('books/',views.BooksViews.as_view({'get': 'list', 'post': 'create'})),
path('books/<int:pk>/',views.BooksViews.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
]
views.py
class BooksViews(ModelViewSet):
queryset= Books.objects.all()
serializer_class=BookSerializers
ModelViewSet里面已经写好了方法 直接用就可以 但是需要重写路由 路由格式变成 as_view({'k': 'v'}) 这样的格式
2.通过ReadOnlyModelViewSet编写只读接口
只读:查询所有 查询单个
views.py
class BooksViews(ReadOnlyModelViewSet):
queryset= Books.objects.all()
serializer_class=BookSerializers
url.py as_view括号里面的请求参数 只能填查询所有 和查询单个的 写多了会报错
urlpatterns = [
path('books/',views.BooksViews.as_view({'get': 'list'})),
path('books/<int:pk>/',views.BooksViews.as_view({'get': 'retrieve'})),
ModelViewSet = mixins.CreateModelMixin+RetrieveModelMixin+UpdateModelMixin+DestroyModelMixin+ListModelMixin+GenericViewSet
GenericviewSet = ViewSetMixin+GenericAPIView
导致路由变了的原因就是ViewSetMixin【没见过的】
ViewSetMixin源码注释:以后配置路由变成了view = MyViewSet.as_view({'get': 'list', 'post': 'create'})这种形式
3.ViewSetMixin源码分析
请求来 -- 路由匹配成功
get请求 -- 匹配成功
books会执行 -- views.BooksViews.as_view({'get': 'list'})
读as_view 这个as_view是ViewSetMixin的as_view
上面的方法都没有 去GenericviewSet里面找
ViewSetMixin在前面 先去这里面找 找到了
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
if not actions:
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`")
#如果没有传actions直接抛异常 路由的as_view中不传字典 直接报错
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if 'get' in actions and 'head' not in actions:
actions['head'] = actions['get']
self.action_map = actions
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
return csrf_exempt(view)
#去除了csrf校验
#路由匹配成功执行views.BooksViews.as_view({'get': 'retrieve'})),
#本质是执行viewSetMixin -- as_view -- view()
def view(request, *args, **kwargs):
self.action_map = actions
#actions是传入的字典{'get': 'retrieve'}
for method, action in actions.items():
#第一次循环:method:get action:retrieve
handler = getattr(self, action)
#通过反射去视图类中反射action对应的方法
setattr(self, method, handler)
#把method:get请求方法 handler:list方法
#视图类的对象的get方法 变成list方法
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
#dispatch 是APIView的
总结:
1.只要继承ViewSetMixin的视图类 路由写法就变了(重写了as_view)
2.路由变成需要传入地点的映射方法
只要传入actions 以后访问get就是访问list以此类推
3.其他一样
4.以后视图类中的方法名可以任意命名 只要做好映射就可以
4.from rest_framework.viewsets包下的类
ModelViewSet:
5个视图扩展类+ViewSetMixin + GenericAPIView
ReadOnlyModelViewSet:
2个视图扩展类 (读全部 读单个+ViewSetMixin+GenericAPIView
ViewSetMixin:
魔法 重写了as_view只要继承他 路由就变成字典映射方法
ViewSet:
ViewSetMixin+ APIView
GenericViewSet:
ViewSetMixin+ GenericAPIView
#重点:
以后像继承APIView但是想变路由写法 (视图类中方法名任意命名)要继承ViewSet 在里面写方法
以后想要继承GenericAPIView 但是想变路由写法(视图类中方法名任意命名) 要继承GenericViewSet 在里面写方法
5.视图层大总结
1.两个视图基类
APIView GenericAPIView
2.5个试图扩展类 不是视图类 必须配合GenericAPIView一起使用
from rest_framework.mixins import
ListModelMixin,
CreateModelMixin,
RetrieveModelMixin,
UpdateModelMixin,
DestroyModelMixin
3.9个试图子类 是视图类 用到哪个继承那个就可以
from rest_framework.generics import
DestroyAPIView
RetrieveAPIView
RetrieveDestroyAPIView
RetrieveUpdateAPIView
RetrieveUpdateDestroyAPIView
ListAPIView
ListCreateAPIView
CreateAPIView
UpdateAPIView
4.视图集
ModelViewSet:
路由写法变了 字典、映射方法 只需要写两行 5个接口都能用
ReadOnlyModelViewSet:
路由写法变了,字典、映射方法(只写两个actions 查所有和查一个) 只需要写两行,2个只读接口都有了
ViewSetMixin:
不是视图类 重写了as_view 路由写法变了 变成映射
ViewSet:
ViewSetMixin+ APIView
GenericViewSet:
ViewSetMixin+ GenericAPIView
# 举例子:发送短信接口,视图类叫SendView,方法叫send_sms,路由配置变了
get--->send_sms
class SendView(ViewSet):
def send_sms(self,request):
路由系统
1.自动生成路由
# drf 由于继承ViewSetMinxin类,路由写法变了
-原生+drf,以后的路由写法,可能会有如下情况(三种情况)
-path('books/', views.BookView.as_view()
-path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'}))
-自动生成
drf提供了两个路由类,继承ModelViewSet后,路由可以自动生成
使用步骤:
1.导入路由类
from rest_framework.routers import SimpleRouter,DefaultRouter
2.实例化的到一个对象
router = SimpleRouter() 少 常用
router = DefaultRouter()多
3.注册
router.register('books',views.Bookviews,'books')
4.在urlpatterns中注册 两种方式
方式1:
urlpatterns += router.url
方式2:
include:path(api/v1/,include(router.url))
底层实现:自动生成路由
本质是自动做映射 能够自动成的前提是 视图类中要有5个方法的某个甚至更多
get--->list
get---->retrieve
put---->update
post---->create
delete---->destory
ModelViewSet和ReadOnlyModelViewSet:
可以自动生成路由
9个视图子类+ViewSetMixin:
才可以自动生成
GenericAPIView+5个试图扩展类+ViewSetMixin:
才能自动生成
action装饰器
action 写在视图类的方法上 可以自动生成路由
使用步骤:
1.写在视图类方法上
class BooksViews(ModelViewSet):
@action(methods=['GET'], detail=False, url_name='send', url_path='send')
#method指定请求方法 可以传多个
#deatil 只能传: #True:带id的路径 :send/2/send_sms/
#Flase:不带id的路径::send/send_sms/
def send(self,request):
return Response('123')
'以后看到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