Django rest-framework视图家族
目录
视图家族
- 视图类 GenericAPIView:包含两大视图类(APIView、GenericAPIView)
- 视图工具类 mixins:包含五大工具类,六大工具方法
- 工具视图类 generics:包含九大工具视图类,一堆mixins工具类与GenericAPIView视图基类组合
- 视图集 viewsets:可自定义映射关系
from rest_framework import views,generics,mixins,viewsets
视图类 GenericAPIView
两大视图类:APIView、GenericAPIView
APIView
from rest_framework.views import APIView
- 继承基本View,拥有View所有的功能
- 重写了as_view()方法,禁用了csrf认证
- 重写dispatch,封装请求、响应、渲染、异常、解析、三大认证模块
- 封装一堆属性,可完成视图类的局部配置
GenericAPIView
from rest_framework.generics import GenericAPIView
- 继承APIView,拥有APIView所有的功能
- 提供
get_queryset
方法:配置queryset
类属性,群查获取QuerySet对象 - 提供
get_object
方法:配置lookup_url_kwarg
类属性,单查获取单个对象 - 提供
get_serializer
方法:配置serializer_class
类属性,提供序列化类并使用自定义的序列化类序列化
总结:GenericAPIView就是在APIView基础上额外提供了三个方法和三个类属性,如果不配合视图工具类,则体现不出来优势所在
使用它的好处:视图中的增删改查逻辑其实大差不差,但操作的资源不一致(操作的资源指的是models模型类和序列化类),将资源形成配置,操作逻辑一致,就可以完成封装
使用GenericAPIView类
- 继承GenericAPIView类
- 配置对哪个表进行操作
- 配置使用哪个序列化类
群查
from rest_framework.generics import GenericAPIView
class ViewGenericAPIView(GenericAPIView):
# 配置关联表的属性
# 如果只写models.Car.objects的话那就是manager对象,不是QuerySet对象
queryset = models.Car.objects.filter(is_delete=False).all()
# 配置使用的序列化类
serializer_class = serializer.CarModelSerializer
# 群查
def get(self,request,*args,**kwargs):
# 帮我们去表里面拿数据
car_query = self.get_queryset()
# 帮我们去序列化
car_ser = self.get_serializer(car_query,many=True)
return APIResponse(results=car_ser.data)
单查
from rest_framework.generics import GenericAPIView
class ViewGenericAPIView(GenericAPIView):
# 配置关联表的属性
# 如果只写models.Car.objects的话那就是manager对象,不是QuerySet对象
queryset = models.Car.objects.filter(is_delete=False).all()
# 配置使用的序列化类
serializer_class = serializer.CarModelSerializer
# 配置查询的条件为pk,单查走pk过滤的条件
lookup_url_kwarg = 'pk'
# 单查
def get(self,request,*args,**kwargs):
car_obj = self.get_object()
car_ser = self.get_serializer(car_obj)
return APIResponse(results=car_ser.data)
视图工具类 mixins
- 在GenericAPIView的基础上提供了五个类,六个方法六大接口(单查、群查、单增、单整体改、单局部改、单删)
- 使用的时候需要配合继承GenericAPIView类
五大工具类
- RetrieveModelMixin:单查类
- ListModelMixin:群查类
- CreateModelMixin:单增类
- UpdateModelMixin:单整体和单局部改类
- DestroyModelMixin:单删类
六大工具方法
- retrieve:单查方法
- list:群查方法
- create:单增方法
- update:单整体改方法
- partial_update:单局部改方法
- destroy:单删方法
使用mixins的六大工具方法
- 继承GenericAPIView类
- 配置对哪个表进行操作
- 配置使用哪个序列化类
from rest_framework import mixins
class ViewMixinsAPIView(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializer.CarModelSerializer
lookup_url_kwarg = 'pk'
# 单查群查
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
return self.retrieve(request, *args, **kwargs)
return self.list(request, *args, **kwargs)
# 单增
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
# 单整体改
def put(self,request, *args, **kwargs):
return self.update(request, *args, **kwargs)
# 单局部改
def patch(self,request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
# 单删
def delete(self,request, *args, **kwargs):
# django中的删除是真正的删除
# 删除接口一般是自己实现重写到的,因为真正的业务不需要真正的删除
pass
# django源代码中是真的删除
return self.destroy(request, *args, **kwargs)
工具视图类 generics
工具类加视图类的组合,只要继承工具该类,就有响应的方法,
- 帮我们将不同的mixins工具类与GenericAPIView视图类进行组合,我们不再需要继承GenericAPIView类
- 不同的组合封装成一个个的类,实现对应的请求方法(get、post、put、patch、delete)
随后就是用单查就继承单查的接口,用群查就继承群查的接口即可。
使用generics的工具类实现接口
- 配置对哪个表进行操作
- 配置使用哪个序列化类
from rest_framework import generics
class ViewGenericsAPIView(generics.RetrieveAPIView,
generics.ListAPIView,
generics.CreateAPIView,
generics.UpdateAPIView,
generics.DestroyAPIView):
# 单查和群查只能使用一个get,具体调用哪个要看继承的顺序
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializer.CarModelSerializer
lookup_url_kwarg = 'pk'
# 有删除需求的接口继承DestroyAPIView,重写destroy完成字段的删除
def destroy(self, request, *args, **kwargs):
pass
视图集 viewsets
- 重写as_view方法,增加action参数(可以完成路由层的请求方法映射关系)
- 可以在路由层中自定义请求方法的映射关系
使用viewsets的视图集类实现接口
- 配置对哪个表进行操作
- 配置使用哪个序列化类
可自定义路由层中请求方法的映射关系来实现接口
路由层
url(r'^v5/cars/$', views.ViewViewsetsAPIView.as_view({
"get":"list",
"post":"create"
})),
url(r'^v5/cars/(?P<pk>\d+)/$', views.ViewViewsetsAPIView.as_view({
"get":"retrieve",
"put":"update",
"patch":"partial_update",
"delete":"destroy"
})),
视图层
from rest_framework import viewsets
# 视图集类
class ViewViewsetsAPIView(viewsets.ModelViewSet):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializer.CarModelSerializer
lookup_url_kwarg = 'pk'
完善viewsets的视图集类实现接口
以上的步骤我们继承视图集的ModelViewSet类实现了六大接口,但是从实际开发角度分析有很多不合理的点:
- 没有群增,群整体改,群局部改,群删四个接口
- 删除只做字段的修改
- 响应的结果只有数据,没有数据状态码和状态信息
所以针对以上问题,我们解决一下:
路由层配置
url(r'^v5/cars/$', views.ViewViewsetsAPIView.as_view({
"get":"list",
"post":"create",
"put":"many_update",
"patch":"many_partial_update",
"delete":"many_destroy"
})),
url(r'^v5/cars/(?P<pk>\d+)/$', views.ViewViewsetsAPIView.as_view({
"get":"retrieve",
"put":"update",
"patch":"partial_update",
"delete":"destroy"
})),
实现群增,群整体改,群局部改,群删四个接口
视图层配置
# 群整体改
def many_update(self,request,*args,**kwargs):
try:
pks = []
for dic in request.data:
pks.append(dic.pop('pk'))
car_query = models.Car.objects.filter(is_delete=False, pk__in=pks).all()
if len(pks) != len(car_query):
raise Exception('pk对象不存在')
except Exception as e:
return Response({'detail': '%s' % e}, status=400)
car_ser = self.get_serializer(instance=car_query,data=request.data,many=True)
car_ser.is_valid(raise_exception=True)
car_obj = car_ser.save()
return APIResponse(results=self.get_serializer(car_obj,many=True).data)
# 群局部改
def many_partial_update(self,request,*args,**kwargs):
try:
pks = []
for dic in request.data:
pks.append(dic.pop('pk'))
car_query = models.Car.objects.filter(is_delete=False, pk__in=pks).all()
if len(pks) != len(car_query):
raise Exception('pk对象不存在')
except Exception as e:
return Response({'detail': '%s' % e}, status=400)
car_ser = self.get_serializer(instance=car_query,data=request.data,many=True,partial=True)
car_ser.is_valid(raise_exception=True)
car_obj = car_ser.save()
return APIResponse(results=self.get_serializer(car_obj,many=True).data)
# 群删
def many_destroy(self,request,*args,**kwargs):
pks = request.data
try:
rows = models.Car.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
except:
return APIResponse(1, '数据有误')
if rows:
return APIResponse(msg='删除成功')
return APIResponse(1, '删除失败')
# 群增和单增必须使用同一个接口,都要走create方法,重写create方法,使用逻辑拆分
def create(self, request, *args, **kwargs):
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方法完成对字段的修改
def destroy(self, request, *args, **kwargs):
car_obj = self.get_object()
if not car_obj:
return APIResponse(1,msg="删除失败")
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)
# 重写retrieve方法,单查有状态码和状态信息
def retrieve(self, request, *args, **kwargs):
response = super().retrieve(request, *args, **kwargs)
return APIResponse(results=response.data)
路由组件(了解)
使用SimpleRouter结合视图组件进行路由配置
from django.conf.urls import url,include
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
# 将所有路由与ViewSet视图类的都可以注册,会产生'^v5/cars/$' 和 '^v5/cars/(?P<pk>[^/.]+)/$'的url
router.register('v5/cars',views.ViewViewsetsAPIView,basename='car')
urlpatterns = [
# 第一种添加子列表方式
url(r'^', include(router.urls)),
]
# 第二种添加子列表方式
# urlpatterns.extend(router.urls)
路由组件源码部分
如果想实现其他映射关系的话,修改源码就行了
routes = [
# List route.
# 群增,如果想要在url中奖请求方式映射关系改变的话,可以重写这个
Route(
url=r'^{prefix}{trailing_slash}$',
mapping={
'get': 'list',
'post': 'create'
},
name='{basename}-list',
detail=False,
initkwargs={'suffix': 'List'}
),
# Dynamically generated list routes. Generated using
# @action(detail=False) decorator on methods of the viewset.
DynamicRoute(
url=r'^{prefix}/{url_path}{trailing_slash}$',
name='{basename}-{url_name}',
detail=False,
initkwargs={}
),
# Detail route.
Route(
url=r'^{prefix}/{lookup}{trailing_slash}$',
mapping={
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
},
name='{basename}-detail',
detail=True,
initkwargs={'suffix': 'Instance'}
),
# Dynamically generated detail routes. Generated using
# @action(detail=True) decorator on methods of the viewset.
DynamicRoute(
url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$',
name='{basename}-{url_name}',
detail=True,
initkwargs={}
),
]
或者自定义路由对象
from rest_framework.routers import Route, DynamicRoute, SimpleRouter as DRFSimpleRouter
class SimpleRouter(DRFSimpleRouter):
routes = [
# List route. /资源s/
Route(
url=r'^{prefix}{trailing_slash}$',
mapping={
'get': 'list', # 群查
'post': 'create', # 单增、群增
'put': 'many_update', # 群整改
'patch': 'many_partial_update', # 群局改
'delete': 'many_destroy', # 群删
},
name='{basename}-list',
detail=False,
initkwargs={'suffix': 'List'}
),
# Dynamically generated list routes. Generated using
# @action(detail=False) decorator on methods of the viewset.
DynamicRoute(
url=r'^{prefix}/{url_path}{trailing_slash}$',
name='{basename}-{url_name}',
detail=False,
initkwargs={}
),
# Detail route. /资源s/(pk)/
Route(
url=r'^{prefix}/{lookup}{trailing_slash}$',
mapping={
'get': 'retrieve', # 单查
'put': 'update', # 单整改
'patch': 'partial_update', # 单局改
'delete': 'destroy' # 单删
},
name='{basename}-detail',
detail=True,
initkwargs={'suffix': 'Instance'}
),
# Dynamically generated detail routes. Generated using
# @action(detail=True) decorator on methods of the viewset.
DynamicRoute(
url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$',
name='{basename}-{url_name}',
detail=True,
initkwargs={}
),
]
# 对外提供router对象
router = SimpleRouter()
# eg: router.register('users', UserModelViewSet, basename='user')