1230 视图家族类
视图家族
1. 两个基类 views 视图类
1.1 APIView
rest_framework.views.APIView
APIView是REST framework提供的所有视图的基类,继承自Django的View父类。
View: 将请求方式与视图类的同名方法建立映射,完成请求响应
特点
- 拥有view的所有功能
- 重写了as_view
局部禁用csrf
- 重写dispatch
请求,响应,渲染,异常,解析,三大认证
- 一系列类属性
可以完成视图类的局部配置
与View
的不同
APIView
与View
的不同之处在于:
- 传入到视图方法中的是REST framework的`Request`对象,而不是Django的`HttpRequeset`对象;
- 视图方法可以返回REST framework的`Response`对象,视图会为响应数据设置(render)符合前端要求的格式;
- 任何`APIException`异常都会被捕获到,并且处理成合适的响应信息;
- 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
在APIView
中仍以常规的类视图定义方法来实现get() 、post(),或者其他请求方式的方法。
from rest_framework.views import APIView
from rest_framework.response import Response
# url(r'^books/$', views.BookListView.as_view()),
class BookListView(APIView):
def get(self, request):
books = BookInfo.objects.all()
serializer = BookInfoSerializer(books, many=True)
return Response(serializer.data)
1.2 GenericAPIView
from rest_framework.generics import GenericAPIView
特点
继承自APIVIew,增加了对于列表视图和详情视图可能用到的通用支持方法。主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。
- 继承APIView,所以拥有APIView的所有
- get_queryset方法,配置queryset类属性,提供类相关的models
- 在第二条基础上,get_object方法,配置lookup_url_kwargs类属性,提供视图类相关的具体model
- get_serializer方法,配置serializer_class类属性,提供视图类相关的序列化对象
提供的方法
- get_object()
get_object(self) 返回详情视图所需的模型类数据对象,默认使用lookup_field参数来过滤queryset。 在试图中可以调用该方法获取详情信息的模型类对象。
若详情访问的模型类对象不存在,会返回404。
该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问。
- get_serializer()
返回序列化器对象,被其他视图或扩展类使用,如果我们在视图中想要获取序列化器对象,可以直接调用此方法。
注意,在提供序列化器对象的时候,REST framework会向对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用。
- get_queryset()
返回视图使用的查询集,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写
代码
.all()不能直接写到objects结束,因为objects结束时,不是QuerySet对象,而是Manager对象,但.all()和.filter()后一定会是QuerySet对象
from rest_framework.generics import GenericAPIView
class CarGenericAPIView(GenericAPIView):
queryset = models.Car.objects.all()
serializer_class = serializers.CarModelSerializer
# GenericAPIView的群查
def get(self,request,*args,**kwargs):
# car_query = models.Car.objects.filter(is_delete=False).all()
# 1. 封装了获取queryset对象的get方法,首先要定义
car_query = self.get_queryset()
# car_ser = serializers.CarModelSerializer(instance=car_query,many=True)
# 2. 封装了序列化类,但是也需要指定serializer_class序列化类
car_ser = self.get_serializer(instance=car_query,many=True)
return APIResponse(results=car_ser.data)
# 单查
def get(self,request,*args,**kwargs):
# pk = kwargs.get('pk')
# car_obj = models.Car.objects.filter(is_delete=False,pk=pk).first()
# 通过get_object获取单查的obj对象
car_obj = self.get_object()
# car_ser = serializers.CarModelSerializer(car_obj)
car_ser = self.get_serializer(car_obj)
return APIResponse(results=car_ser.data)
lookup_url_kwarg
属性
# 1.url单查时系统默认设置有名分组是'pk',也可更改并自己配置(唯一键的字段名)
lookup_url_kwarg = 'id' # 使用id当做分组查询url
总结
- GenericAPIView就是在APIView基础上额外提供了三个方法,三个类属性,如果不配合视图工具类,体现不出优势
- 目的:视图中的增删改查逻辑相似,但操作的资源不一致,操作资源就是操作 资源对象们、资源对象以及资源相关的序列化类,
- 将这三者形成配置,那操作逻辑就一致,就可以进行封装
2. 五个扩展类 mixins 视图工具类
from rest_framework.mixins import
RetrieveModelMixin,
ListModelMixin,
CreateModelMixin,
UpdateModelMixin,
DestroyModelMixin
使用
# mixins视图工具类
from rest_framework.generics import mixins
class CarMixinGenericAPIView(mixins.ListModelMixin,mixins.CreateModelMixin,GenericAPIView):
queryset = models.Car.objects.all()
serializer_class = serializers.CarModelSerializer
# 群查
def get(self,request,*args,**kwargs):
# ListModelMixin中的list与之前的一样,所以直接使用
return self.list(request,*args,**kwargs)
# 单查 (单查与群查不能同时存在)
def get(self,request,*args,**kwargs):
# ListModelMixin中的list与之前的一样,所以直接使用
return self.retrieve(request,*args,**kwargs)
# 增
def post(self,request,*args,**kwargs):
# CreateModelMixin的create与之前的一样,所以直接使用
return self.create(request,*args,**kwargs)
特点
五个类,六个方法
- 要配合GenericAPIView类使用,将单查、群查、单增,单整体改,单局部改,单删六个接口,封装成retrieve、list、create、update、partial_update、destroy六个方法
原因
六个方法的实现体,调用的方法就是GenericAPIView提供的,所以要配合GenericAPIView类使用
2.1 ListModelMixin (群查)
列表视图扩展类,提供list(request, \*args, \**kwargs)
方法快速实现列表视图,返回200状态码。
该Mixin的list方法会对数据进行过滤和分页。
# 群查
def get(self,request,*args,**kwargs):
# ListModelMixin中的list与之前的一样,所以直接使用
return self.list(request,*args,**kwargs)
2.2 CreateModelMixin (单增)
创建视图扩展类,提供create(request, \*args, \**kwargs)
方法快速实现创建资源的视图,成功返回201状态码。
如果序列化器对前端发送的数据验证失败,返回400错误。
# 增
def post(self,request,*args,**kwargs):
# CreateModelMixin的create与之前的一样,所以直接使用
return self.create(request,*args,**kwargs)
2.3 RetrieveModelMixin (单查)
详情视图扩展类,提供retrieve(request, \*args, \**kwargs)
方法,可以快速实现返回一个存在的数据对象。
如果存在,返回200, 否则返回404。
class BookDetailView(RetrieveModelMixin, GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request, pk):
return self.retrieve(request)
2.4 UpdateModelMixin (局部单改)
更新视图扩展类,提供update(request, \*args, \**kwargs)
方法,可以快速实现更新一个存在的数据对象。
同时也提供partial_update(request, \*args, \**kwargs)
方法,可以实现局部更新。
成功返回200,序列化器校验数据失败时,返回400错误。
源代码:
class UpdateModelMixin(object):
"""
Update a model instance.
"""
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save()
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
2.5 DestroyModelMixin (删除)
直接删除数据对象,不推荐
删除视图扩展类,提供destroy(request, \*args, \**kwargs)
方法,可以快速实现删除一个存在的数据对象。
成功返回204,不存在返回404。
class DestroyModelMixin(object):
"""
Destroy a model instance.
"""
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance):
instance.delete()
3. 可用子类视图 generics 工具视图类
from rest_framework import generics
- 帮我们将不同个数不同种类的mixins与GenericAPIView进行组合
- 不同的组合帮我们实现好对应的get、post、put、patch、delete方法
- 需要我们自己配置三个类属性即可:
queryset、serializer_class、lookup_url_kwarg
# 工具视图类 (最常用的视图)
from rest_framework.generics import CreateAPIView,ListAPIView,ListCreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView
# CreateAPIView 继承了 mixins.CreateModelMixin,GenericAPIView
# CreateAPIView中帮我们写了post方法 (单增)
# ListAPIView写了get方法 (群查)
# 而ListCreateAPIView中也包含,所以直接继承,单查,群查,单改,单删一起继承即可
class CarListCreateAPIView(CreateAPIView,ListAPIView,ListCreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializers.CarModelSerializer
# url单查时系统默认设置有名分组是'pk',也可更改并自己配置(唯一键的字段名)
lookup_url_kwarg = 'id' # 使用id当做分组查询url
# 1.单查时都不需要写get方法继承使用,直接配置queryset与serializer_class即可
# 2. 删除时,destroy方法默认是字段删除,所以需要重写destroy方法
# 有删除需求的接口继承DestroyAPIView,重写destroy方法完成字段的删除(is_delet=False)
def destroy(self, request, *args, **kwargs):
pass
# 单独完成单查接口
from rest_framework.generics import RetrieveAPIView
class CarRetrieveAPIView(RetrieveAPIView):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializers.CarModelSerializer
lookup_url_kwarg = 'id' # 使用id当做分组查询url
# 单独完成群查接口
from rest_framework.generics import ListAPIView
class CarListAPIView(ListAPIView):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializers.CarModelSerializer
1)CreateAPIView
提供 post 方法
继承自: GenericAPIView、CreateModelMixin
2)ListAPIView (群查)
提供 get 方法
继承自:GenericAPIView、ListModelMixin
# 单独完成群查接口
from rest_framework.generics import ListAPIView
class CarListAPIView(ListAPIView):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializers.CarModelSerializer
3)RetireveAPIView (单查)
提供 get 方法
继承自: GenericAPIView、RetrieveModelMixin
# 单独完成单查接口
from rest_framework.generics import RetrieveAPIView
class CarRetrieveAPIView(RetrieveAPIView):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializers.CarModelSerializer
lookup_url_kwarg = 'id' # 使用id当做分组查询url
4)DestoryAPIView
提供 delete 方法
继承自:GenericAPIView、DestoryModelMixin
5)UpdateAPIView
提供 put 和 patch 方法
继承自:GenericAPIView、UpdateModelMixin
6)RetrieveUpdateAPIView
提供 get、put、patch方法
继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
7)RetrieveUpdateDestoryAPIView 单查,单整体改,单局部改,单删
提供 get、put、patch、delete方法
继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
8)ListCreateAPIView (群查和单增)
推导代码
class CarView(APIView):
def get(self):
obj,
ser,
response,
class CarView(GenericAPIView): # 不会出现,中间产物
queryset,
serializer_class,
lookup_url_kwarg,
def get(self):
obj,
ser,
response,
class CarView(RetrieveModelMixin, GenericAPIView): # 自定义组合,可以超过九种
queryset,
serializer_class,
lookup_url_kwarg,
def get(self):
self.retrieve()
class CarView(RetrieveAPIView): # 最终产物,系统只提供了九种组合,RetrieveAPIView是其中一种
queryset,
serializer_class,
lookup_url_kwarg,
4. 视图集 ViewSet
from rest_framework.viewsets import ModelViewSet
六大功能接口
# 视图集
# 1.viewsetmixin重写了as_view方法,解决单查群查问题
from rest_framework.viewsets import ModelViewSet
# ModelViewSet中继承了viewsetmixin和工具视图类的所有类
class CarModelViewSet(ModelViewSet):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializers.CarModelSerializer
# 实现了六大接口
# 自定义其他接口的方法
def destroy(self,request,*args,**kwargs):
pass
# 2.在as_view中设置单查群查的接口方法
# 将所有请求方法与响应方法名的映射关系交给用户自己配置(自定义映射关系)
url(r'^v5/cars/$', views.CarModelViewSet.as_view({
'get':'list',
'post':'my_post',
})),
url(r'v5/cars/(?P<pk>\d+)/$', views.CarModelViewSet.as_view({
'get':'retrieve',
'put':'update',
'patch':'partial_update',
'delete':'destroy'
})),
4.1 常用视图集父类
1) ViewSet
继承自APIView
,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。
在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
2)GenericViewSet
继承自GenericAPIView
,作用也与GenericAPIVIew类似,提供了get_object、get_queryset等方法便于列表视图与详情信息视图的开发。
3)ModelViewSet
继承自GenericAPIVIew
,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
4)ReadOnlyModelViewSet
继承自GenericAPIVIew
,同时包括了ListModelMixin、RetrieveModelMixin。
4.2 action属性
使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:
- list() 提供一组数据
- retrieve() 提供单个数据
- create() 创建数据
- update() 保存数据
- destory() 删除数据
ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。
视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。如:
class BookInfoViewSet(viewsets.ViewSet):
def list(self, request):
...
def retrieve(self, request, pk=None):
...
在设置路由时,我们可以如下操作
urlpatterns = [
url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}),
url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'})
]
action属性
在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪个。
例如:
# 动态设置序列化器def get_serializer_class(self):
if self.action == 'create':
return OrderCommitSerializer
else:
return OrderDataSerializer
4.3 视图集自定义群操作
# 视图集
from rest_framework.viewsets import ViewSetMixin, GenericViewSet, ViewSet, ModelViewSet
class CarReadOnlyAPIView(RetrieveModelMixin, ListModelMixin, GenericViewSet):
# def many_get(self, request, *args, **kwargs):
# return self.list(request, *args, **kwargs)
#
# def single_get(self, request, *args, **kwargs):
# return self.retrieve(request, *args, **kwargs)
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializers.CarModelSerializer
# 六大接口
class CarModelViewSet(ModelViewSet):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializers.CarModelSerializer
# 分析:从实际开发角度分析不合理点
# 1)没有群增,群整体改,群局部改,群删四个接口
# 2)删除操作视图集默认走的destroy方法是将资源从数据库中删除,通常一个做字段is_delete字段修改表示删除
# 3)响应的结果只有数据,没有数据状态码和状态信息
# 解决1,
# 群整体改,群局部改,全删三个接口可以独立成三个方法
def many_update(self, request, *args, **kwargs):
return APIResponse(msg='这个地方是群整体改,你会写!')
def many_partial_update(self, request, *args, **kwargs):
return APIResponse(msg='这个地方是群局部改,你会写!')
def many_destroy(self, request, *args, **kwargs):
return APIResponse(msg='这个地方是群删,你会写!')
# 群增与单增必须公用一个接口,都要走create方法 - 重写create方法,用逻辑进行拆分
def create(self, request, *args, **kwargs):
request_data = request.data
if isinstance(request_data, list):
car_ser = self.get_serializer(data=request_data, many=True)
car_ser.is_valid(raise_exception=True)
car_obj = car_ser.save()
return APIResponse(msg='群增成功', results=self.get_serializer(car_obj, many=True).data)
return super().create(request, *args, **kwargs)
# 解决2,destroy方法是完成is_delete字段值修改 - 重写destroy方法,自定义实现体
def destroy(self, request, *args, **kwargs):
car_obj = self.get_object()
car_obj.is_delete = True
car_obj.save()
return APIResponse(msg='删除成功')
# 解决3,让群查有状态码和状态信息 - 重写list方法
def list(self, request, *args, **kwargs):
response = super().list(request, *args, **kwargs)
return APIResponse(results=response.data)
4.4 总结
核心:
视图集都继承了 ViewSetMixin类,该类重写了as_view方法,相比APIView的as_view方法,额外多出了一个参数actions
as_view({'get': 'list'}) 传入的{'get': 'list'}就被actions接受收,原理是将get请求映射给视图类的list函数进行处理
1)为什么有GenericViewSet和ViewSet两个视图集基类
GenericViewSet(ViewSetMixin, GenericAPIView),该分支严格满足资源接口
ViewSet(ViewSetMixin, APIView),该分支满足的接口与资源Model类关系不是特别密切:登录接口、短信验证码接口
2)ReadOnlyModelViewSet,ModelViewSet两个视图集子类,就是做个一堆mixin与GenericViewSet相结合,自己在urls文件中配置as_view设置映射关系
5. 路由Routers
对于视图集ViewSet我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息。
REST framework提供了两个router
- SimpleRouter
- DefaultRouter
使用方式
1.创建router对象,并注册视图集,例如
# 路由层:外面会遇到这种写法,看到了要认识
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('v7/cars', views.CarModelViewSet, basename='car')
# router.register('books', views.BookModelViewSet, basename='book')
# router.register('users', views.UserModelViewSet, basename='user')
register(prefix, viewset, base_name)
- prefix 该视图集的路由前缀
- viewset 视图集
- base_name 路由名称的前缀
如上述代码会形成的路由如下:
^books/$ name: book-list
^books/{pk}/$ name: book-detail
2.添加路由数据
可以有两种方式:
urlpatterns = [
url(r'', include(router.urls))
]
# urlpatterns.extend(router.urls)
或
urlpatterns = [
...
]
urlpatterns += router.urls