You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

关键修改点

  1. 判断视图类型:通过issubclass(view_class, ViewSetMixin)确认当前视图是DRF视图集
  2. 实例化视图并初始化:调用view_instance.setup(request, *view_args, **view_kwargs),这是DRF视图初始化的核心步骤,会自动解析请求并设置action属性
  3. 获取正确的action:通过view_instance.action拿到当前请求对应的动作,精准判断是否为create

说明

  • 该方法仅临时实例化视图获取action,不会干扰后续视图的正常执行流程
  • 对于使用POST请求的@action自定义方法,其action值为自定义名称(而非create),会被自动排除在限制逻辑外

内容的提问来源于stack exchange,提问作者Michael Lin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 05:43:15