Проблема с декоратором Django и промежуточным ПО

Мой декоратор выглядит следующим образом

def require_feature(feature_name):
    def decorator(view_func):
        print(f"process_view - view_func: {view_func}")  # Debugging

        @wraps(view_func)
        def _wrapped_view(request, *args, **kwargs):
            return view_func(request, *args, **kwargs)

        _wrapped_view.required_feature = feature_name
        return _wrapped_view

    return decorator

а промежуточное программное обеспечение выглядит следующим образом

class EntitlementMiddleware(MiddlewareMixin):
    def __init__(self, get_response) -> None:
        self.get_response = get_response

    def __call__(self, request) -> Any:
        if request.user.is_authenticated:
            request.user.features = get_user_features(request.user)
        else:
            request.user.features = []
        return self.get_response(request)

    def process_view(self, request, view_func, view_args, view_kwargs):
        print(f"process_view - view_func: {view_func}")  # Debugging
        required_feature = getattr(view_func, "required_feature", None)
        if required_feature and not request.user.features.filter(name=required_feature):
            raise PermissionDenied("You do not have access to this feature")

        return None  # if none is returned then view is called normally

    def process_response(self, request, response):
        return response

и вот как я использую его в своем наборе представлений

class MyViewSet(ValidatePkMixin, viewsets.ViewSet):
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]
    queryset = My.objects.all()

    @method_decorator(require_feature("Basic"))
    def list(self, request):
      pass

декоратор устанавливает required_feature при запуске сервера. промежуточное ПО вызывается, когда я делаю вызов /get, но это

required_feature = getattr(view_func, "required_feature", None)

возвращает None

что я здесь упускаю?

Вы проверяете из middleware функцию(метод) dispatch, которая будет вызываться из middleware. А вы оформляете совершенно другую функцию(метод) list.

В Django при работе с Generic CBVs или ViewSets запрос проходит через middlewares, а через Class.as_view() возвращается запрос-диспетчер. Для обоих вариантов - для Generic CBVs и для ViewSets - это:

class View:
    ...
    def dispatch(self, request, *args, **kwargs):
        return handler # exactly here will be called your cls.list 
    ...
Вернуться на верх