上一章我们已经学习了如何使用genericapiview实现过滤、排序、分页功能;
这次学习的是如何使用Mixins的各种具体通用类简化代码量
一、自己定义mixins通用类
1、在视图类里,感觉有很多代码都是重复的,是不是可以把公用逻辑提取出来单独封装呢?
答案是可以的!
我们在utils目录下单独创建一个文件mixins.py提取公共部分
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from rest_framework import status
from rest_framework.response import Response
class ListModelMixin:
def list(self, request, *args, **kwargs):
qs = self.get_queryset()
qs = self.filter_queryset(qs)
page_queryset = self.paginate_queryset(qs)
if page_queryset is not None:
serializer_obj = self.get_serializer(instance=page_queryset, many=True)
return self.get_paginated_response(serializer_obj.data)
serializer_obj = self.get_serializer(instance=page_queryset, many=True)
return Response(serializer_obj.data, status=status.HTTP_200_OK)
class CreateModelMixin:
def create(self, request, *args, **kwargs):
serializer_obj = self.get_serializer(data=request.data)
serializer_obj.is_valid(raise_exception=True)
serializer_obj.save()
return Response(serializer_obj.data, status=status.HTTP_201_CREATED)
class RetrieveModelMixin:
def retrieve(self, request, *args, **kwargs):
pro = self.get_object()
serializer_obj = self.serializer_class(instance=pro)
return Response(serializer_obj.data, status=status.HTTP_200_OK)
class UpdateModelMixin:
def update(self, request, *args, **kwargs):
# 查出对应id的数据
query_data = self.get_object()
serializer_obj = self.serializer_class(instance=query_data, data=request.data)
serializer_obj.is_valid()
# 保存更新的数据
serializer_obj.save()
return Response(serializer_obj.data, status=status.HTTP_201_CREATED)
class DestroyModelMixin:
def destroy(self, request, *args, **kwargs):
ret = {
"msg": "删除成功!"
}
# 根据id查出对应数据
query_data = self.get_object()
# 删除指定数据
query_data.delete()
# 一般删除数据的输出为None
return Response(ret, status=status.HTTP_204_NO_CONTENT)
2、然后在视图文件中导入
from utils import mixins
class ProjectsDetailViews(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
queryset = ProjectsModel.objects.all()
serializer_class = ProjectModelSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
# 更新数据
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
# 删除数据
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
# class ProjectsViews(APIView):
class ProjectsViews(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
# 指定当前类视图需要使用的查询集
queryset = ProjectsModel.objects.all()
# 指定当前类视图需要使用的序列化器类
serializer_class = ProjectModelSerializer
# lookup_field = 'Id'
# 声明需要使用的引擎类
filter_backends = [filters.SearchFilter,
filters.OrderingFilter
]
# 定义需要过滤的字段
search_fields = ['name', 'id']
# 定义需要排序的字段
ordering_fields = ['id', 'name']
# 声明需要使用的分页引擎
pagination_class = PageNumberPagination
# 查询全部数据
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
# 创建数据
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
如图所示,视图类中继承的父类中的顺序要注意:
- GenericAPIView只能放在最后位置,因为mixins.py公共逻辑文件中很多方法都是需要继承GenericAPIView的,这里面涉及到mro算法
- 比如获取列表方法中的get_queryset,我们并没有在misins文件中导入对应的包,当在当前文件中找不到这个方法时,就会依次去其他父类中查找,最后在GenericAPIView这个父类中找到
- 如果GenericAPIView放在最前面,那么可能会导致有些方法找不到
二、使用rest_framework中的mixins通用类
首先我们可以进入到GenericAPIView视图类里去看下,发现里面已经定义好了很多通用接口类
再去看具体的接口请求类里面看,发现继承的是GenericAPIView和rest_framework中mixins.py里的ListModelMixin等一些类(如下图例子)
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
GenericAPIView中的ListAPIView中,返回的是rest_framework中mixins.py里的ListModelMixin的list方法
class ListModelMixin: """ List a queryset. """ def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data)
drf框架已经内部定义了一些已经封装好的通用类,而且可以看出代码逻辑和我们自己写的大体上差不多,所以我们可以放心的使用drf框架内的这些封装类以及方法
导入rest_framework中的mixins包:
# from utils import mixins
from rest_framework import mixins
因为 我们之前定义的通用类名/方法名与drf内置的通用类名/方法名一致,所以视图集内的其他代码可以不用变动
三、使用rest_framework的generics.py文件中的通用类
- 因为通用类中也继承了GenericAPIView,所以视图类里可以不用再继承了,可以删除
- 因为通用类CreateAPIView和ListAPIView分别继承了rest_framework中的mixins.CreateModelMixin和mixins.ListModelMixin,分别调用了create方法和list方法,所以在视图类里可以不用再自己写各种接口方法了(下面代码块注释掉的部分)
class ProjectsViews(generics.CreateAPIView,
generics.ListAPIView,
):
# 指定当前类视图需要使用的查询集
queryset = ProjectsModel.objects.all()
# 指定当前类视图需要使用的序列化器类
serializer_class = ProjectModelSerializer
# lookup_field = 'Id'
# 声明需要使用的引擎类
filter_backends = [filters.SearchFilter,
filters.OrderingFilter
]
# 定义需要过滤的字段
search_fields = ['name', 'id']
# 定义需要排序的字段
ordering_fields = ['id', 'name']
# 声明需要使用的分页引擎
pagination_class = PageNumberPagination
# 查询全部数据
# def get(self, request, *args, **kwargs):
# return self.list(request, *args, **kwargs)
#
# # 创建数据
# def post(self, request, *args, **kwargs):
# return self.create(request, *args, **kwargs)
上面的继承类也可以直接继承generics里的ListCreateAPIView类
class ProjectsViews(generics.ListCreateAPIView):
四、类视图继承的父类区别
1、继承APIView
- 如果两个类视图合并,会出现两个get方法会冲突的问题
- 如果使用Mixin中提供的拓展方法(action动作),当前DRF是无法识别这些action
- class ProjectViewSet(APIView):
2、继承ViewSet视图集
- 两个类视图可以合并,尤其是有2个get方法的类视图
- 支持这些action,并在定义url路由时,可以在as_view({"请求方法名": "action名称"})
- 例如as_view({'get':'retrieve','put':'update','delete':'destroy','patch':'partial_update'})
- as_view({'get':'list','post':'create',})
- 继承ViewSetMixin()(ViewSetMixin提供的能力,改写as_view(),以便它接受' actions '关键字来执行将HTTP方法绑定到资源上的操作。),views.APIView
- 需要自定义action
- 并且不支持分页、过滤、排序功能
- class ProjectViewSet(viewsets.ViewSet):
class ProjectsViewSet(viewsets.ViewSet):
# 指定当前类视图需要使用的查询集
queryset = ProjectsModel.objects.all()
# 指定当前类视图需要使用的序列化器类
serializer_class = ProjectModelSerializer
# lookup_field = 'Id'
# 声明需要使用的引擎类
filter_backends = [filters.SearchFilter,
filters.OrderingFilter
]
# 定义需要过滤的字段
search_fields = ['name', 'id']
# 定义需要排序的字段
ordering_fields = ['id', 'name']
# 声明需要使用的分页引擎
pagination_class = PageNumberPagination
def retrieve(self, request, *args, **kwargs):
# 需要自定义action
pass
def list(self, request, *args, **kwargs):
pass
def put(self, request, *args, **kwargs):
pass
def destroy(self, request, *args, **kwargs):
pass
def create(self, request, *args, **kwargs):
pass
def partial_update(self, request, *args, **kwargs):
pass
3、继承GenericViewSet通用视图集
两个类视图可以合并,尤其是有2个get方法的类视图
继承 ViewSetMixin, generics.GenericAPIView
ViewSetMixin提供了路由识别action的能力
支持分页、过滤、排序功能(因为有继承generics.GenericAPIView)
可以先声明queryset、serializer_class
未提供action方法
class ProjectViewSet(viewsets.GenericViewSet):
class ProjectsViewSet(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
mixins.CreateModelMixin,
viewsets.GenericViewSet):
# 指定当前类视图需要使用的查询集
queryset = ProjectsModel.objects.all()
# 指定当前类视图需要使用的序列化器类
serializer_class = ProjectModelSerializer
# lookup_field = 'Id'
# 声明需要使用的引擎类
filter_backends = [filters.SearchFilter,
filters.OrderingFilter
]
# 定义需要过滤的字段
search_fields = ['name', 'id']
# 定义需要排序的字段
ordering_fields = ['id', 'name']
# 声明需要使用的分页引擎
pagination_class = PageNumberPagination
# 因为继承了mixins里的各种action方法,所以自定义的action就可以不用写了
# 如果请求方法为get -> retrieve方法
# def retrieve(self, request, *args, **kwargs):
# pass
# 如果请求方法为get -> list方法
# def list(self, request, *args, **kwargs):
# pass
#
# def update(self, request, *args, **kwargs):
# pass
#
# def destroy(self, request, *args, **kwargs):
# pass
#
# def create(self, request, *args, **kwargs):
# pass
#
# def partial_update(self, request, *args, **kwargs):
# pass
🔼因为继承了mixins里的各种action方法,所以自定义的action就可以不用写了(上面被注释的代码!)
上面继承了太多的 mixins,我们可以找个全部继承了这些类的子类 来继承,这就是ModelViewSet模型视图集类
最后可以简化为:
class ProjectsViewSet(viewsets.ModelViewSet):