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



我要做的是检测登录用户的类型,然后将.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/"

问题是您不能向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

在花了几个小时弄清楚发生了什么之后,发现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"
],
}

相关内容

  • 没有找到相关文章

最新更新