[英]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.在花了几个小时弄清楚发生了什么之后,发现SimpleJWT的JWTAuthentication.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.