简体   繁体   English

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

[英]How to modify django's request.user in a Middleware?

What I'm trying to do is to detect the type of logged-in user and then setting a .profile parameter to request.user , so I can use it by calling request.user.profile in my views.我想要做的是检测登录用户的类型,然后将.profile参数设置为request.user ,这样我就可以通过在我的视图中调用request.user.profile来使用它。

To do this, I've wrote a Middleware as follows:为此,我编写了一个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)

Now I can access request.user.profile in process_view however it does not exists in my views and is causing an AttributeError stating that 'User' object has no attribute 'profile' .现在我可以在process_view中访问request.user.profile但是它在我的视图中不存在并且导致AttributeError指出'User' object has no attribute 'profile'

Seems my request.user is being overwritten somewhere before hitting the view.似乎我的request.user在点击视图之前在某处被覆盖。


Note that I'm using Django Rest Framework, here is my view:请注意,我使用的是 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

Here is my settings.py :这是我的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/"

The problem is that you cannot add new properties to the User class.问题是您无法向User class 添加新属性。

instead try to add the property directly to the request like this而是尝试像这样将属性直接添加到请求中

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

Then in your function based view:然后在基于 function 的视图中:

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

The only difference between function based view and class based view is that the decorator will receive request argument instead of self .基于 function 的视图和基于 class 的视图之间的唯一区别是装饰器将接收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

Your class should look like this:您的 class 应如下所示:

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

After spending hours to figure out what is going on, turned out that SimpleJWT 's JWTAuthentication.authenticate() method gets called just before the request hits the View, overwriting the request.user attribute.在花了几个小时弄清楚发生了什么之后,发现SimpleJWTJWTAuthentication.authenticate()方法在请求到达视图之前被调用,覆盖了request.user属性。

So instead of trying to add the profile to the request.user using a middleware, I ended-up customizing JWTAuthentication.authentication() method:因此,我没有尝试使用中间件将配置文件添加到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 : settings.py

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM