Files
django-vue3-admin-gd/backend/utils/custom_model_viewSet.py

179 lines
6.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from rest_framework import viewsets, status
from rest_framework.response import Response
# from utils.decorators import idempotent
from utils.export_mixin import ExportMixin
class CustomModelViewSet(viewsets.ModelViewSet, ExportMixin):
"""
自定义ModelViewSet提供以下增强功能
- 基于动作的序列化器选择
- 基于动作的权限控制
- 标准化响应格式
- 软删除支持
- 批量操作支持
"""
# 动作到序列化器类的映射
action_serializers = {}
# 动作到权限类的映射
action_permissions = {}
# 软删除字段名
soft_delete_field = 'is_deleted'
# 是否支持软删除
enable_soft_delete = False
def get_required_permission(self):
# 约定system:menu:create
app_label = self.queryset.model._meta.app_label
model_name = self.queryset.model._meta.model_name
action = self.action # 'create', 'update', 'destroy', 'list', 'retrieve'
# 只对增删改查等操作做权限控制
action_map = {
'create': 'create',
'update': 'edit',
'partial_update': 'edit',
'destroy': 'delete',
'list': 'query',
'retrieve': 'query',
}
if action in action_map:
perm_action = action_map[action]
else:
perm_action = action # 如 sync、import、export
return f"{app_label}:{model_name}:{perm_action}"
def get_permissions(self):
permissions = super().get_permissions()
required_code = self.get_required_permission()
if required_code:
from utils.permissions import HasButtonPermission
perm = HasButtonPermission()
# 动态设置 required_permission
perm.required_permission = required_code
permissions.append(perm)
return permissions
def get_serializer_class(self):
"""根据当前动作获取序列化器类"""
return self.action_serializers.get(
self.action,
super().get_serializer_class()
)
def list(self, request, *args, **kwargs):
"""重写列表视图,支持软删除过滤"""
queryset = self.get_queryset()
# 应用软删除过滤
if self.enable_soft_delete:
queryset = queryset.filter(**{self.soft_delete_field: False})
# 应用搜索和过滤
queryset = self.filter_queryset(queryset)
# 判断是否传了 page 参数
if 'page' in request.query_params:
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
# 没有 page 参数,返回全部数据
serializer = self.get_serializer(queryset, many=True)
return self._build_response(
data=serializer.data,
message="ok",
status=status.HTTP_200_OK
)
def retrieve(self, request, *args, **kwargs):
"""重写详情视图,支持软删除检查"""
instance = self.get_object()
# 检查软删除状态
if (self.enable_soft_delete and
hasattr(instance, self.soft_delete_field) and
getattr(instance, self.soft_delete_field)):
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = self.get_serializer(instance)
return self._build_response(
data=serializer.data,
message="Object retrieved successfully",
status=status.HTTP_200_OK
)
# @idempotent(timeout=10) # 幂等性装饰器,防止重复提交
def create(self, request, *args, **kwargs):
"""重写创建视图,支持批量创建"""
is_many = isinstance(request.data, list)
if is_many:
serializer = self.get_serializer(data=request.data, many=True)
else:
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
return self._build_response(
data=serializer.data,
message="ok",
status=status.HTTP_200_OK,
)
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return self._build_response(
message="ok",
status=status.HTTP_200_OK,
)
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 self._build_response(
data=serializer.data,
message="ok",
status=status.HTTP_200_OK,
)
def _build_response(self, code=0, message="成功", data=None, status=status.HTTP_200_OK):
"""
构建标准化API响应格式
参数说明:
- code: 业务状态码0表示成功非0表示错误
- message: 状态描述信息
- data: 响应数据可为None
- status: HTTP状态码默认200
"""
# 构建基础响应结构
response_data = {
"code": code,
"message": message
}
# 仅当data不为None时添加到响应中
if data is not None:
response_data["data"] = data
# 移除可能的空值如message为空字符串
response_data = {k: v for k, v in response_data.items() if v is not None and v != ""}
# 返回DRF的Response对象
return Response(
data=response_data,
status=status,
content_type="application/json"
)