import json
from django.http import HttpResponse, JsonResponse
# 子应用视图
from django.views import View
from rest_framework import status, filters, viewsets
from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter
from rest_framework.generics import GenericAPIView, ListCreateAPIView, RetrieveUpdateDestroyAPIView
from rest_framework.renderers import JSONRenderer
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import mixins
from projects.models import Projects
# from projects.serializers import ProjectSerializer
from projects.serializers import PorjectModelSerializer
from utils.pagination import PageNumberPagination
# class ProjectsView(View):
# class ProjectsView(APIView):
# class ProjectsView(GenericAPIView):
# class ProjectsView(mixins.ListModelMixin,
# mixins.CreateModelMixin,
# GenericAPIView):
# class ProjectsViewSet(ListCreateAPIView,
# RetrieveUpdateDestroyAPIView):
# class ProjectsViewSet(viewsets.ViewSet):
# class ProjectsViewSet(mixins.ListModelMixin,
# mixins.CreateModelMixin,
# mixins.RetrieveModelMixin,
# mixins.UpdateModelMixin,
# mixins.DestroyModelMixin,
# viewsets.ViewSet):
# class ProjectsViewSet(mixins.ListModelMixin,
# mixins.CreateModelMixin,
# mixins.RetrieveModelMixin,
# mixins.UpdateModelMixin,
# mixins.DestroyModelMixin,
# viewsets.GenericViewSet):
class ProjectsViewSet(viewsets.ModelViewSet):
"""
将2个视图类合并为1个视图类
继承了视图集父类ViewSet:
1.在定义url条目时,支持给as_view()传递字典参数。
2.ViewSet继承了ViewSetMixin, views.APIView.
3.继承了ViewSetMixin,所以可以给as_view()传递字典参数
4.继承了views.APIView,所以具备APIView的所有授权,限流等功能。
"""
queryset = Projects.objects.all()
serializer_class = PorjectModelSerializer
# 2。 类视图中指定可用于过滤查询的字段search_fields = [xxx]
search_fields = ['^full_name', 'id']
# 配置过滤器类
filter_backends = [filters.SearchFilter, OrderingFilter]
# 排序字段
ordering_fields = ['full_name', 'leader', 'id']
# 指定自定义的分页器引擎(先导入)
pagination_class = PageNumberPagination
@action(methods=["GET"], detail=False, url_path="ns", url_name="chuntian")
def names(self, request, *args, **kwargs):
"自定义action,只获取所有项目id和项目名称,且不分页"
queryset = self.get_queryset()
project_list = []
for project_query_set in queryset:
project_obj = {
"id": project_query_set.id,
"full_name": project_query_set.full_name
}
project_list.append(project_obj)
return Response(project_list, status=status.HTTP_200_OK)
@action(methods=["GET"], detail=True)
def interfaces(self, *args, **kwargs):
"自定义获取指定项目下的接口信息"
project_obj = self.get_object()
interfaces_querset = project_obj.interfaces_set.all()
interfaces_data = [{"id": interface_obj.id, "name": interface_obj.name}
for interface_obj in interfaces_querset]
return Response(interfaces_data, status=status.HTTP_200_OK)
需求1:GET /projects/projects/ 返回已定义的序列化器中定义的所有字段,
GET projects/projects/<int:pk>/ 返回特定内容的特定字段,比如详情中不返回“leader","is_execute"字段:
解决方式1: 通过重写父类的get_serializer_class()方法控制使用不同的序列化器来实现
重新定义一个序列化器类,让使用list()方法的时候使用原序列化器类,让获取详情数据retrieve()方法时使用新定义的序列化器类
通过重写父类的get_serializer_class()方法,可以选择性地使用序列化器类.
请求过去时,会在视图集中保存action参数,这个参数就是前端的请求映射方法.
1。可以重写父类的get_serializer_class()方法,用于为不同的action提供 不一样的序列
化器类。
2。在视图集对象中,可以使用action属性获取当前访问的action方法名称。
效果: 没有“leader”,“is_execute”字段
方式1总结:可以通过重写get_serializer_class()方法,控制不同请求使用不同的序列化器类.
解决方式2: 重写父类的retrieve()方法,自定义返回字段.
分析:当前需求中要求调用retrieve()方法返回字定义的字段,list()方法还是返回原序列化器类中控制的字段,说明当前父类的retrieve()方法有一点点不满足需求,大部分是满足需求的,所以直接重写父类的retrieve()方法控制返回的字段.
观察:父类的retrieve()方法,发现返回的是一个Response(serializer.data)对象,那么我们可以重写时,在返回Response对象前将不需要的字段pop掉,然后返回pop掉多余字段的Response就Ok了.
response.data为原生返回的dict类型数据
去除不需要的leader,is_execute字段数据
前端验证
方式2总结:当父类的方法绝大部分满足需求,少部分不满足时可以直接重写该方法.
案例1:
优化names方法
1.定义序列化器类,只输出id,full_name字段
2. 重写get_serializer_class方法控制前端路由映射为names的请求使用的序列化器类为自定
义的ProjectNamesModelSerializer进行序列化输出
names方法中使用get_serializer()方法获取序列化器类
3.前端验证:
需求2: 继承父类后,自定义控制是否过滤(不是所有的方法都过滤)
names若直接调用list()方法,则数据返回具备搜索过滤的功能
前端验证是否具备过滤
重写filter_queryset()方法,控制names方法在调用list()方法后也不具备搜索过滤能力
前端验证
names方法的搜索过滤功能被取消了
需求2: 继承父类后,自定义控制是否分页(不是所有的都分页)
将names在调用list()方法后取消其分页功能
因为list()方法中使用到了分页的操作,直接调用父类中的list()方法也会实现分页
前端验证
定制取消names在调用list()方法后的分页能力:
list()方法中有调用分页器
查看分页器代码
重写分页器,使names在调用list方法后不会被分页
前端验证:此时调用names方法既没有搜索过滤能力了,也没有分页能力了
需求3: 当用户登录后有权限能获得的查询集数据为全部数据,当用户未登录时,没有权限,获取的查询集数据就只有full_name中包含“chuntian"关键字的数据集.
获取查询集可以用父类的self.get_queryset()方法
重写get_queryset()方法,让names方法调用时,只能获取到包含“chuntian"关键字的数据集.
前端验证
附代码:
# projects/views.py
import copy
import json
from django.http import HttpResponse, JsonResponse
# 子应用视图
from django.views import View
from rest_framework import status, filters, viewsets
from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter
from rest_framework.generics import GenericAPIView, ListCreateAPIView, RetrieveUpdateDestroyAPIView
from rest_framework.renderers import JSONRenderer
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import mixins
from projects.models import Projects
# from projects.serializers import ProjectSerializer
from projects.serializers import PorjectModelSerializer, PorjectModelSerializer2, ProjectNamesModelSerializer
from utils.pagination import PageNumberPagination
# class ProjectsView(View):
# class ProjectsView(APIView):
# class ProjectsView(GenericAPIView):
# class ProjectsView(mixins.ListModelMixin,
# mixins.CreateModelMixin,
# GenericAPIView):
# class ProjectsViewSet(ListCreateAPIView,
# RetrieveUpdateDestroyAPIView):
# class ProjectsViewSet(viewsets.ViewSet):
# class ProjectsViewSet(mixins.ListModelMixin,
# mixins.CreateModelMixin,
# mixins.RetrieveModelMixin,
# mixins.UpdateModelMixin,
# mixins.DestroyModelMixin,
# viewsets.ViewSet):
# class ProjectsViewSet(mixins.ListModelMixin,
# mixins.CreateModelMixin,
# mixins.RetrieveModelMixin,
# mixins.UpdateModelMixin,
# mixins.DestroyModelMixin,
# viewsets.GenericViewSet):
class ProjectsViewSet(viewsets.ModelViewSet):
"""
list:
获取项目列表数据
retrieve:
获取项目详情数据
update:
更新项目数据
create:
创建项目
destroy:
删除项目数据
names:
获取项目名称
interfaces:
获取项目下的接口数据
"""
queryset = Projects.objects.all()
serializer_class = PorjectModelSerializer
# 2。 类视图中指定可用于过滤查询的字段search_fields = [xxx]
search_fields = ['^full_name', 'id']
# 配置过滤器类
filter_backends = [filters.SearchFilter, OrderingFilter]
# 排序字段
ordering_fields = ['full_name', 'leader', 'id']
# 指定自定义的分页器引擎(先导入)
pagination_class = PageNumberPagination
@action(methods=["GET"], detail=False)
def names(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
@action(methods=["GET"], detail=True)
def interfaces(self, *args, **kwargs):
project_obj = self.get_object()
interfaces_querset = project_obj.interfaces_set.all()
interfaces_data = [{"id": interface_obj.id, "name": interface_obj.name}
for interface_obj in interfaces_querset]
return Response(interfaces_data, status=status.HTTP_200_OK)
#
def get_serializer_class(self):
"""
1。可以重写父类的get_serializer_class()方法,用于为不同的action提供
不一样的序列化器类。
2。在视图集对象中,可以使用action属性获取当前访问的action方法名称。
:return:
"""
if self.action == "retrieve":
return PorjectModelSerializer2
elif self.action == "names":
return ProjectNamesModelSerializer
return super().get_serializer_class()
def retrieve(self, request, *args, **kwargs):
response = super().retrieve(request, *args, **kwargs)
response.data.pop("leader")
response.data.pop("is_execute")
return response
def filter_queryset(self, queryset):
if self.action == "names":
# return self.queryset
return self.get_queryset()
return super().filter_queryset(queryset)
def paginate_queryset(self, queryset):
if self.action == "names":
return None
return super().paginate_queryset(queryset)
def get_queryset(self):
if self.action == "names":
return self.queryset.filter(full_name__icontains="chuntian")
return super().get_queryset()
如何定义类视图?
1.1.类视图尽量简化
1.2.类视图根据实际需求选择需要继承的父类视图
1.3.如果DRF中的类视图有提供相应的满足需求功能的逻辑,尽量继承DRF中的类视图,
并使用父类提供的功能.
1.4.如果DRF中的类视图,大多数逻辑能满足需求,可以重写父类中的实现.
1.5.如果DRF中的类视图完全不满足需求,可以直接自定义.