如何在中间件中修改django的request.user?

von4xj4u  于 2022-11-18  发布在  Go
关注(0)|答案(2)|浏览(282)

我尝试做的是检测登录用户的类型,然后将.profile参数设置为request.user,这样我就可以通过在视图中调用request.user.profile来使用它。
为此,我编写了一个Middleware,如下所示:

class SetProfileMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):

        user, token = JWTAuthentication().authenticate(request)
        profile_type = token.payload.get("profile_type", None)

        request.user.profile = User.get_profile(profile_type, request.user)
        request.user.profile_type = profile_type
        
        # Works Here
        print("-" * 20)
        print(type(request.user)) # <class 'django.utils.functional.SimpleLazyObject'>
        print('Process Request ->', request.user.profile)

        response = self.get_response(request)

        # Does not work here
        print("-" * 20)
        print(type(request.user)) #  <class 'users.models.User'>
        print('Process Response ->', request.user.profile)

        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        # Works here
        print("-" * 20)
        print(type(request.user)) # <class 'django.utils.functional.SimpleLazyObject'>
        print('Process View ->', request.user.profile)

现在我可以在process_view中访问request.user.profile,但它不存在于我的视图中,并导致AttributeError声明'User' object has no attribute 'profile'
似乎我的request.user是被覆盖的地方之前击中的看法。
请注意,我使用的是Django Rest框架,以下是我的观点:

class ProfileAPIView(generics.RetrieveUpdateAPIView):
    serializer_class = ProfileSerializer

    def get_object(self):
        obj = self.request.user.profile # Raise the `AttributeError`
        self.check_object_permissions(self.request, obj)
        return obj

下面是我的settings.py

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

LOCAL_MIDDLEWARE = [
    "users.middleware.SetProfileMiddleware",
]

MIDDLEWARE = MIDDLEWARE + LOCAL_MIDDLEWARE

REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
    "DEFAULT_RENDERER_CLASSES": (
        "rest_framework.renderers.JSONRenderer",
        "rest_framework.renderers.BrowsableAPIRenderer",
    ),
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ],
}

SIMPLE_JWT = {
    "SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(minutes=45),
    "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.SlidingToken",),
}

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

AUTH_USER_MODEL = "users.User"

LOGIN_REDIRECT_URL = "admin/"
elcex8rz

elcex8rz1#

问题是您无法将新属性添加到User类。
而是尝试将属性直接添加到请求中,如下所示
request.user_profile = User.get_profile(profile_type, request.user)

def set_profile(view_function):
    
    def decorated_function(request, *args, **kwargs):

        user, token = JWTAuthentication().authenticate(request)
        profile_type = token.payload.get("profile_type", None)

        request.user_profile = User.get_profile(profile_type, request.user)
        request.user_profile_type = profile_type

        return view_function(request, *args, **kwargs)

    return decorated_function # No invocation here

然后在基于函数的视图中:

@api_view(["GET", "PUT"])
@set_profile
def my_view(request):
    request.user_profile # Will not throw attribute error
    ...

基于函数的视图和基于类的视图之间的唯一区别是装饰器将接收request参数而不是self

def set_profile(view_function):
    
    def decorated_function(self, *args, **kwargs):

        user, token = JWTAuthentication().authenticate(self.request)
        profile_type = token.payload.get("profile_type", None)

        self.request.user_profile = User.get_profile(profile_type, self.request.user)
        self.request.user_profile_type = profile_type

        return view_function(self, *args, **kwargs)

    return decorated_function # No invocation here

您的类应如下所示:

class ProfileAPIView(generics.RetrieveUpdateAPIView):
serializer_class = ProfileSerializer

@set_profile
def get_object(self):
    obj = self.request.user_profile
    self.check_object_permissions(self.request, obj)
    return obj
lrpiutwd

lrpiutwd2#

在花了几个小时弄清楚发生了什么之后,发现SimpleJWTJWTAuthentication.authenticate()方法在请求到达视图之前被调用,覆盖了request.user属性。
因此,我没有尝试使用中间件将概要文件添加到request.user,而是结束了对JWTAuthentication.authentication()方法的定制:

class CustomAuth(JWTAuthentication):
    def authenticate(self, request):

        user, token = super().authenticate(request)

        profile_type = token.payload.get("profile_type", None)
        user.profile = User.get_profile((profile_type, user)
        user.profile_type = profile_type

        return user, token

settings.py

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "users.authentication.CustomAuth"
    ],
}

相关问题