一、视图基类
1、APIView
想要使用APIView,需要先导入:
from rest_framework.views import APIView
APIView 继承了 View,同时也进行了升级,APIView 是适合前后端分离的,而 View 是适合前后端不分离的。View 并没有对请求进行处理,而 APIView 会对请求进行处理,将请求体的 JSON 数据转成字典。我们使用 DRF 设计后端,那么 APIView 是最基本的视图类,任何类视图都以 APIView 为基础。
既然 APIView 是最基础的,所以 APIView 也是最灵活的。我们在基础了 APIView 后,还要自己手写get,post,put,delete方法。而且,一个类视图里面,不能写两个及以上的处理同一类型请求的方法,例如,这个类里面不能有两个get方法。如果想要写两个get方法,一个用来获取部分数据,一个用来获取全部数据,那我们需要写到两个类视图里面去。
from .serializer import BookSerializer
from rest_framework.response import Response
from .models import Book
class BookView(APIView):
def get(self, request):
obj_list = Book.objects.all()
serializer=BookSerializer(instance=obj_list,many=True)
return Response({'code': 100, 'msg': '查询成功', 'results':serializer.data})
def post(self, request):
serializer=BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save() # 需要在序列化类中重写 create才能存进去
return Response({'code': 100, 'msg': '保存成功'})
else:
return Response({'code': 101, 'msg': serializer.errors})
# 127.0.0.1:8000/app01/books/1
class BookDetailView(APIView):
def get(self, request, pk):
obj = Book.objects.filter(pk=pk).first()
serializer=BookSerializer(instance=obj)
return Response({'code':100,'msg':'成功','result':serializer.data})
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100, 'msg': '删除成功'})
def put(self, request, pk):
obj=Book.objects.filter(pk=pk).first()
serializer=BookSerializer(instance=obj,data=request.data)
if serializer.is_valid():
serializer.save() # 重写update 触发序列化类的update
return Response({'code':100,'msg':'成功'})
else:
return Response({'code': 100, 'msg': serializer.errors})
2、GenericAPIView
使用 GenericAPIView,需要先导入:
from rest_framework.generics import GenericAPIView
GenericAPIView 继承了 APIView,同时增加了下面的四个方法:
- get_object:返回视图需要的模型类数据对象,主要用来提供给Mixin类使用。通俗来说就是获取单条,根据路由中分组的字段 pk,这个pk可以指定换成别的
- get_serializer:返回序列化器对象,主要用来提供给Mixin类使用,通俗来说就是完成序列化
- get_serializer_class:返回序列化器类,主要用来判断,可以重写,根据不同请求获取不同的序列化类
- get_queryset:返回视图使用的查询集,主要用来提供给Mixin类使用,通俗来说就是返回所有数据
因为这四个方法,可以大大地简化我们的类视图设计时需要的代码量。
当然,我们需要在这个类视图里面加上两个字段,字段如下:
from rest_framework.response import Response
class BookView(GenericAPIView):
# 俩类属性
queryset = Book.objects.all() # 查询所有的数据
serializer_class = BookSerializer # 序列化类
那我们可以尝试用GenericAPIView + 序列化类+Response 写上面的5个接口
from rest_framework.generics import GenericAPIView
class BookView(GenericAPIView):
queryset = Book.objects.all() # 查询所有的数据
serializer_class = BookSerializer # 序列化类
def get(self, request):
obj_list = self.get_queryset() # 使用get_queryset方法获取所有数据,而不要使用self.queryset 属性获取
serializer = self.get_serializer(instance=obj_list, many=True) # 使用序列化类--》直接使用方法:self.get_serializer
return Response({'code': 100, 'msg': '查询成功', 'results': serializer.data})
def post(self, request):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
serializer.save() # 需要在序列化类中重写 create才能存进去
return Response({'code': 100, 'msg': '保存成功'})
else:
return Response({'code': 101, 'msg': serializer.errors})
class BookDetailView(GenericAPIView):
queryset = Book.objects.all() # 查询所有的数据
serializer_class = BookSerializer # 序列化类
def get(self, request, pk):
# 拿单条
obj = self.get_object() # 连pk都没传到函数中,就能拿到单条
serializer = self.get_serializer(instance=obj)
return Response({'code': 100, 'msg': '成功', 'result': serializer.data})
def delete(self, request, pk):
self.get_object().delete()
return Response({'code': 100, 'msg': '删除成功'})
def put(self, request, pk):
obj = self.get_object()
serializer = self.get_serializer(instance=obj, data=request.data)
if serializer.is_valid():
serializer.save() # 重写update 触发序列化类的update
return Response({'code': 100, 'msg': '成功'})
else:
return Response({'code': 100, 'msg': serializer.errors})
### GenericAPIView:
# ----两个类属性:queryset和serializer_class
# ----三个方法:self.get_object() self.get_serializer() self.get_queryset()
- 如果需要写Publish的5个接口,所有代码不变,只需要改两个类属性即可
queryset = Publish.objects.all() # 查询所有的数据
serializer_class = PublishSerializer # 序列化类
但是经过对上面的5个接口处理代码改变的观察,我们发现,代码并没有简化。所以,DRF 为什么搞出 GenericAPIView 来让程序员更麻烦。的确,如果我们只使用 GenericAPIView 的话,的确是自找麻烦。如果我们加上了 Mixin 类的话,GenericAPIView 直接升华。接下来我们就介绍 Mixin 类。
二、视图扩展类(Mixin类)
视图扩展类有5个,分别是:
- ListModelMixin,封装了get请求获取全部数据的代码,其下有list方法
- CreateModelMixin,封装了post请求新增数据的代码,其下有create方法
- DestroyModelMixin,封装了delete请求删除数据的代码,其下有destroy方法
- UpdateModelMixin,封装了put请求修改数据的代码,其下有update方法
- RetrieveModelMixin,封装了get请求获取部分数据的代码,其下有retrieve方法
注意:扩展类必须配合GenericAPIView使用,GenericAPIView必须在前面
扩展类内部的方法,在调用序列化器时,都是使用get_serializer,需要自定义get、post等请求方法,内部实现调用扩展类对应方法即可,比如:
class ClassListView(GenericAPIView, ListModelMixin): queryset = ClassInfo.objects.all() serializer_class = ClassInfoSerializer def get(self, request): return self.list(request)
1、ListModelMixin
- 列表视图扩展类,提供
list(request, *args, **kwargs)
方法快速实现列表视图,返回200状态码。该Mixin的list方法会对数据进行过滤和分页。
class TestView(GenericAPIView, ListModelMixin):
serializer_class = UserSerializer
queryset = User.objects.all()
def get(self, request):
return self.list(request)
2、CreateModelMixin
- 创建视图扩展类,提供
create(request, *args, **kwargs)
方法快速实现创建资源的视图,成功返回201状态码。如果序列化器对前端发送的数据验证失败,返回400错误。
class TestView(GenericAPIView, CreateModelMixin):
serializer_class = UserSerializer
queryset = User.objects.all()
def post(self, request):
print(request.data)
return self.create(request)
3、RetrieveModelMixin
- 详情视图扩展类,提供
retrieve(request, *args, **kwargs)
方法,可以快速实现返回一个存在的数据对象。如果存在,返回200, 否则返回404。
path('test/<int:pk>', views.TestView.as_view(), name='test'),
class TestView(GenericAPIView, RetrieveModelMixin):
serializer_class = UserSerializer
queryset = User.objects.all()
def get(self, request, pk):
return self.retrieve(request, pk)
ps: 当遇到以下异常时,需要在URL末尾添加
/
RuntimeError: You called this URL via PUT, but the URL doesn't end in a slash and you have APPEND_SLASH set. Django can't redirect to the slash URL while maintaining PUT data. Change your form to point to 127.0.0.1:8000/test/3/ (note the trailing slash), or set APPEND_SLASH=False in your Django settings.
4、UpdateModelMixin
- 更新视图扩展类,提供
update(request, *args, **kwargs)
方法,可以快速实现更新一个存在的数据对象。成功返回200,序列化器校验数据失败时,返回400错误。
path('test/<int:pk>', views.TestView.as_view(), name='test'),
class TestView(GenericAPIView, UpdateModelMixin):
serializer_class = UserSerializer
queryset = User.objects.all()
def put(self, request, pk):
return self.update(request, pk)
5、DestroyModelMixin
- 删除视图扩展类,提供
destroy(request, *args, **kwargs)
方法,可以快速实现删除一个存在的数据对象。成功返回204,不存在返回404。
path('test/<int:pk>', views.TestView.as_view(), name='test'),
class TestView(GenericAPIView, DestroyModelMixin):
serializer_class = UserSerializer
queryset = User.objects.all()
def delete(self, request, pk):
return self.destroy(request, pk)
6、基于5个视图扩展类+GenericAPIView+序列化类+Response写接口
- urls.py
path('books/', BookView.as_view()),
path('books/<int:pk>/', BookDetailView.as_view()),
- views.py
from rest_framework.mixins import CreateModelMixin,RetrieveModelMixin,DestroyModelMixin,ListModelMixin,UpdateModelMixin
# CreateModelMixin # 新增
# RetrieveModelMixin # 查询一条
# DestroyModelMixin # 删除
# ListModelMixin # 查询所有
# UpdateModelMixin # 更新一条
class BookView(GenericAPIView,CreateModelMixin,ListModelMixin):
# 俩类属性
queryset = Book.objects.all() # 查询所有的数据
serializer_class = BookSerializer # 序列化类
def get(self, request):
'''
# super().list(request) 代码,就是下面
obj_list = self.get_queryset() # 使用get_queryset方法获取所有数据,而不要使用self.queryset 属性获取
serializer = self.get_serializer(instance=obj_list, many=True) # 使用序列化类--》直接使用方法:self.get_serializer
return Response({'code': 100, 'msg': '查询成功', 'results': serializer.data})
'''
return super().list(request) # ListModelMixin 的list
def post(self, request):
return super().create(request)
class BookDetailView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = Book.objects.all() # 查询所有的数据
serializer_class = BookSerializer # 序列化类
def get(self, request, *args,**kwargs):
return super().retrieve(request, *args,**kwargs)
def delete(self, request, *args,**kwargs):
return super().destroy(request, *args,**kwargs)
def put(self, request, *args,**kwargs):
return super().update(request, *args,**kwargs)
- serializers.py
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model=Book # 与模型表做一一对应
fields='__all__'
# fields=['id','name','publish_detail','authors_list'] # 有个坑,所有字段都必须要写!
extra_kwargs={
'name':{'max_length':8}, # 限制name不能超过8
'publish':{'write_only':True},
'authors':{'write_only':True},
}
# 可以重写字段--以重写的为准
# 这个字段用来做序列化
publish_detail = PublishSerializer(source='publish',read_only=True)
# 这个字段用来做序列化
authors_list = AuthorSerializer(source='authors', many=True,read_only=True)
# 不需要写create了,但是字段必须是:publish和authors
三、视图子类
视图子类就是 Mixin与GenericAPIView 结合,DRF一共有9个Mixin与GenericAPIView结合的类,这些类同时继承了Mixin和GenericAPIView,我们用到哪些Mixin,只需找到下面的对应的类就行。
CreateAPIView:CreateModelMixin+GenericAPIView
ListAPIView:ListModelMixin+GenericAPIView
RetrieveAPIView:RetrieveModelMixin+GenericAPIView
DestroyAPIView:DestroyModelMixin+GenericAPIView
UpdateAPIView:UpdateModelMixin+GenericAPIView
ListCreateAPIView:ListModelMixin+CreateModelMixin+GenericAPIView
RetrieveUpdateAPIView:RetrieveModelMixin+UpdateModelMixin+GenericAPIView
RetrieveDestroyAPIView:RetrieveModelMixin+DestroyModelMixin+GenericAPIView
RetrieveUpdateDestroyAPIView:RetrieveModelMixin+DestroyModelMixin+UpdateModelMixin+GenericAPIView
小方法:
- 如何在不改变继承的类情况下,限制下面类只能发送get请求
- 最开始的View类中有http_method_names列表,可以限制
# views.py
class BookView(RetrieveUpdateDestroyAPIView):
# 在不改变继承的类情况下,限制只能发送get请求
http_method_names = [
'get',
]
queryset = Book.objects.all()
serializer_class = BookSerializer
示例:
# views.py
from rest_framework.generics import CreateAPIView, ListAPIView, RetrieveAPIView, DestroyAPIView, UpdateAPIView
from rest_framework.generics import ListCreateAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView
class BookView(ListCreateAPIView):
# 查询所有和新增一条接口
queryset = Book.objects.all()
serializer_class = BookSerializer
class BookDetailView(RetrieveUpdateDestroyAPIView):
# 查询单个,修改,删除单个接口
queryset = Book.objects.all()
serializer_class = BookSerializer
四、视图集(ViewSet类)
我们在上面部分了解了许多DRF封装过的类,虽然继承这些类,大大简化了我们的代码,但同时也大大地降低了我们代码的灵活性,不能根据需求来修改请求处理方式的代码,或者说会付出很大代价。而且只要继承了APIView,一个视图类里面就不能有两个处理同一请求类型的方法,否则就会报错。不仅如此,我们还是要写get方法,post方法,put方法,delete方法等等。下面我们来介绍4个ViewSet类,用来解决上面的这些问题。
-
ViewSet
-
继承自APIView,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
-
GenericViewSet
- 继承自GenericAPIView,作用也与GenericAPIVIew类似,提供了get_object、get_queryset等方法便于列表视图与详情信息视图的开发。
-
ModelViewSet
- 继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
-
ReadOnlyModelViewSet
- 继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin。
ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。
1、 ViewSet
ViewSet继承了ViewSetMixin和APIView,类视图通过继承ViewSet,作用与APIView基本类似,提供了身份认证、权限校验、流量管理等,也可以实现一个视图类里面有两个处理同一请求类型的方法。很明显,这个功能不是APIView实现的,所以说这个功能是ViewSetMixin实现的。一会回仔细分析ViewSetMixin的源码,探究怎么实现的。
list:提供一组数据
retrieve:提供单个数据
create:创建数据
update:保存数据
destory:删除数据
# views.py
from rest_framework.response import Response
from .serializers import BookSerializer
from app01 import models
from rest_framework.viewsets import ViewSet
class BookViewSet(ViewSet):
def list(self,request):
queryset = Book.objects.all()
serializer = BookSerializer(queryset,many=True)
return Response(serializer.data)
def retrieve(self,request,pk=None):
queryset = Book.objects.all()
user = get_object_or_404(queryset, pk=pk)
serializer = BookSerializer(user)
return Response(serializer.data)
视图集中的处理方法不再对应请求方式(get、post等)命名,而是以对应的操作(action)命名。在进行URL配置时,需要明确指明某个请求方式请求某个URL地址时,对应的是视图集中的哪个处理函数。
# urls.py
urlpatterns = [
path('books/', BookViewSet.as_view({'get': 'list'})),
path('books/<int:pk>', BookViewSet.as_view({'get': 'retrieve'})),
]
ps:通常,我们不会使用该类。因为需要全部重写。
如果要使用ViewSet,那么将要重写下面的所有方法,这并不比APIView高明多少,但是当你的业务非常复杂的时候,确实可以选择重写ViewSet来实现最大的灵活性。
2、GenericViewSet
上面的代码又回到了最初的APIView时代,所以我们可以使用GenericViewSet来简化。
GenericViewSet继承自 GenericAPIView 与 ViewSetMixin,可以直接搭配Mixin扩展类使用,作用也与GenericAPIVIew类似,提供了get_object、get_queryset等方法便于列表视图与详情信息视图的开发。在GenericAPIView中,没有提供任何动作action方法,需要我们自己覆盖该类并混合所需的混合类,或者明确定义操作实现action方法。
# views.py
from rest_framework.viewsets import GenericViewSet
from booktest.models import Book
from app01.serializers import BookModelSerializer
from rest_framework.response import Response
from rest_framework.mixins import ListModelMixin,RetrieveModelMixin
class BookGenericViewSet(GenericViewSet,ListModelMixin,RetrieveModelMixin):
# GenericViewSet 继承了GnericAPIView 所以必须要有以下两个参数
queryset = Book.objects.all() # 指明当前操作的模型数据是什么,
serializer_class = BookInfoModelSerializer # 指明用的是什么序列化器
def list(self,request):
# 获取所有数据
return self.list(request) # 当前类的方法list,继承了GenricAPIVIew
def retrieve(self,request,pk):
# 获取一条数据
return self.retrieve(request,pk)
# urls.py
urlpatterns = [
path('books/', views.BookGenericViewSet.as_view({
'get': 'list',
})),
path('books/<int:pk>/', views.BookGenericViewSet.as_view({
'get': 'retrieve',
}))
]
3、ModelViewSet
(1)使用
ModelViewSet继承自GenericViewSet,同时包括ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
ModelViewSet用起来就显得非常方便了,我们不需要实现各种方法了,只需要重写属性,然后配置路由即可。
首先我们需要一个序列化器,如下:
from rest_framework import serializers
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
使用ModelViewSet,就要先导入:
from rest_framework.viewsets import ModelViewSet
然后我们创建一个类视图并继承ModelViewSet:
class BookModelViewSet(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
接着我们的路由就可以改成下面这样:
# urls.py
from django.urls import path
from app01.views import BookModelViewSet
urlpatterns = [
path("books/", BookModelViewSet.as_view({"get": "list", "post": "create"})),
path("books/<int:pk>/", BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
]
上面的路由对应着ListModelMixin的list方法, CreateModelMixin的create方法, DestroyModelMixin的destroy方法, UpdateModelMixin的update方法和RetrieveModelMixin的retrieve方法。
(2)定制查询一条的返回格式
# views.py
class BookView(ModelViewSet):
# 查询所有和新增一条接口
queryset = Book.objects.all()
serializer_class = BookSerializer
def retrieve(self, request, *args, **kwargs):
response=super().retrieve(request, *args, **kwargs)
# 从response中取出响应体内容---> response.data
return Response({'code':100,'msg':'查询单条成功','result':response.data})
4、ReadOnlyModelViewSet
ReadOnlyModelViewSet继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。可以用来查所有和查单条。
# views.py
from rest_framework.viewsets import ReadOnlyModelViewSet
class BookView(ReadOnlyModelViewSet):
queryset = Book.objects.all()
serializer_class =BookSerializer
# urls.py
urlpatterns = [
path('books/', views.BookReadOnlyModelViewSet.as_view({
'get': 'list',
})),
path('books/<int:pk>/', views.BookReadOnlyModelViewSet.as_view({
'get': 'retrieve',
}))
]
5、视图集中添加其他方法
在视图集中,除了默认的action处理方法之外,还可以添加额外的其他处理方法。例如:
# views.py
from rest_framework.viewsets import ReadOnlyModelViewSet
class BookView(ViewSet):
queryset = Book.objects.all()
serializer_class =BookSerializer
def other(self, request, pk):
user = Book.objects.get(pk=pk)
serializer = BookSerializer(user)
return Response(serializer.data)
# urls.py
path('test/', views.BookView.as_view({
'get': 'list',
})),
path('test/<int:pk>/', views.TestView.as_view({
'get': 'retrieve',
})),
path('test/other/(?P<pk>\d+)/$', views.TestView.as_view({
'get': 'other'
}))
其实这个功能也是ViewSetMixin实现的,那么我们就一起来看一下这个类的源码:
这个类的方法有很多,我们挑一些关键的看一下
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
# 跟APIView的as_view差不多
# actions must not be empty,如果为空抛异常
if not 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 = cls(**initkwargs)
# actions 咱们传入的字典--> 映射关系
# 加入传进来的是{'get': 'list', 'post': 'create'}
for method, action in actions.items():
# method=get action=list
# method=post action=create
# 视图类对象中反射:list 字符串--> 返回了 list 方法
# handler就是list方法
handler = getattr(self, action)
# 把handler:list方法 通过反射--> 放到了视图类的对象中
# method:get
# 视图类的对象中有个get--> 本质是list
setattr(self, method, handler)
return self.dispatch(request, *args, **kwargs) # APIView的dispatch
return csrf_exempt(view)
五、总结
- 视图类:GenericAPIView继承了APIView
- 视图扩展类:有5个方法 ---> 继承了5个视图扩展类
- ListModelMixin,封装了get请求获取全部数据的代码,其下有list方法
- CreateModelMixin,封装了post请求新增数据的代码,其下有create方法
- DestroyModelMixin,封装了delete请求删除数据的代码,其下有destroy方法
- UpdateModelMixin,封装了put请求修改数据的代码,其下有update方法
- RetrieveModelMixin,封装了get请求获取部分数据的代码,其下有retrieve方法
- 视图子类:九个视图子类 ---> Mixin与GenericAPIView结合的类
- 根据不同的需求继承不同的类
- 视图集:四种视图集,ModelViewSet最方便
- ViewSet---> 继承了ViewSetMixin和APIView---> 必须ViewSetMixin在前,因为属性查找在前
- GenericViewSet---> 继承了 GenericAPIView 与 ViewSetMixin
- ModelViewSet---> 继承了GenericViewSet与五个视图扩展类(ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin)
- ReadOnlyModelViewSet---> 继承了GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。
- 核心:ViewSetMixin类---> 只要继承它---> 路由写法就必须加action---> action是请求方式和视图类中方法的映射关系
- 以后只要继承ViewSetMixin的视图类
- as_view 必须加action做映射
- 视图类中,可以写任意名的方法,只要做好映射,就能执行
- ViewSetMixin源码分析---> 通过重写as_view使得路由写法变了
六、如何选择视图类
1、 APIView
- 如果后续,写个接口,不需要用序列化类,不跟数据库打交道,可以选择这个类
- 例如:发送短信接口,发送邮件接口
2、GenericAPIView
- 如果后续,写个接口,要做序列化或跟数据库打交道,就可以继承他
- 例如:登陆接口,注册接口
3、5个视图扩展类,必须配合 GenericAPIView使用
- 写5个接口或之一来使用
- 例如
class UserView(GenericAPIView,ListModelMixin)
queryset = User.objects.all()
serializer_class = RegisterSerializer
def get(request):
return super().list(request)
4、9个视图子类
- 如果要写5个接口之一或之几,直接使用9个视图子类
- 例如:
class BookView(CreateAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 写以上代码即可
5、ViewSet
- 原来继承APIView,但是想自动生成路由或路由映射,就继承他
- 例如:发送短信接口,发送邮件接口
6、GenericViewSet
- 原来继承GenericAPIView,但是想自动生成路由或路由映射,就继承他
- 例如:登陆接口,注册接口
7、ModelViewSet
- 如果5个接口都要写,而且想自动生成路由或路由映射,就继承它
8、扩展写法
- class BooView(GenericViewSet,ListModelMixin):
- class BooView(ViewSetMixin,GenericAPIView,ListModelMixin):
- class BooView(ViewSetMixin,ListAPIView):