我要做的是检测登录用户的类型,然后将.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
在花了几个小时弄清楚发生了什么之后,发现SimpleJWT的JWTAuthentication.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"
],
}