如何在Django REST framework中间件中获取action属性?
解决Django中间件中无法正确获取DRF视图集action的问题
问题
需要在中间件中检查DRF视图集的action,仅限制create方法(而非其他使用POST请求的@action自定义方法),但当前在中间件中获取action时,要么返回None,要么抛出AttributeError: 'function' object has no attribute 'action'错误。
原因
DRF视图集的action是实例属性,仅在视图类被实例化并执行setup方法后才会被赋值。而中间件的process_view方法执行时,视图类尚未完成实例化,直接访问view_func.cls.action是在访问类属性,自然无法获取到正确的action值。
解决方案
在中间件中手动实例化视图类并执行setup方法,以此获取正确的action属性。具体修改如下:
修改后的中间件代码
import json from django.http import JsonResponse from rest_framework.viewsets import ViewSetMixin from myproject.apps.project.models import Project, Data, Decision # 根据实际路径调整 class MyMiddleware: EXEMPT_URLS = ['some url'] # 豁免URL列表 def __init__(self, get_response): self.get_response = get_response def get_record_count(self, viewset_name): model_mapping = { 'projects': Project, 'datas': Data, 'decisions': Decision } model_class = model_mapping.get(viewset_name) return model_class.objects.count() if model_class else 0 def __call__(self, request): response = self.get_response(request) return response def process_view(self, request, view_func, view_args, view_kwargs): if request.path_info.lstrip('/') in self.EXEMPT_URLS: return None if request.method != 'POST': return None resolver_match = request.resolver_match if not resolver_match: return JsonResponse({'message': 'URL does not match any view'}, status=404) # 获取视图类并判断是否为DRF视图集 view_class = getattr(view_func, 'cls', None) if view_class and issubclass(view_class, ViewSetMixin): # 实例化视图并执行setup方法,初始化action属性 view_instance = view_class() view_instance.setup(request, *view_args, **view_kwargs) action = view_instance.action if action == 'create': viewset_name = resolver_match.url_name.split('-')[0] # 加载许可证数据 with open('.license.json', 'r') as license_file: license_data = json.load(license_file) permission_data = license_data.get(viewset_name, {}) if not permission_data.get('enabled', False): return JsonResponse({'message': 'Viewset is not enabled'}, status=403) limitation_number = permission_data.get('number', 0) if limitation_number == -1: # 无数量限制 return None db_record_count = self.get_record_count(viewset_name) if db_record_count >= limitation_number: return JsonResponse({'message': 'Limitation exceeded'}, status=403) return None
关键修改点
- 判断视图类型:通过
issubclass(view_class, ViewSetMixin)确认当前视图是DRF视图集 - 实例化视图并初始化:调用
view_instance.setup(request, *view_args, **view_kwargs),这是DRF视图初始化的核心步骤,会自动解析请求并设置action属性 - 获取正确的action:通过
view_instance.action拿到当前请求对应的动作,精准判断是否为create
说明
- 该方法仅临时实例化视图获取
action,不会干扰后续视图的正常执行流程 - 对于使用POST请求的
@action自定义方法,其action值为自定义名称(而非create),会被自动排除在限制逻辑外
内容的提问来源于stack exchange,提问作者Michael Lin
相关产品推荐
相关产品推荐

