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.
To do this, I've wrote a Middleware
as follows:
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'
.
Seems my request.user
is being overwritten somewhere before hitting the view.
Note that I'm using Django Rest Framework, here is my view:
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
:
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/"
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.
So instead of trying to add the profile to the request.user
using a middleware, I ended-up customizing JWTAuthentication.authentication()
method:
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"
],
}