Override UserManager in django
Asked Answered
E

2

11

How can I use a custom manager for the auth_user class in django?

In my django project, I'm using auth_user and I have a basic profile class. In every page of my site, I use some user and profile data, so every user query should join profile.

I wanted to use select_related in the get_query_set() method in a custom manager, but I cannot find any proper way to define one, or to override the existing UserManager. Any ideas?

Note: I don't want to override the user model. Or, to be more precise, I already overrode it in different proxy models. I want this custom manager to be used in every proxy model.

Extinctive answered 29/6, 2011 at 9:16 Comment(0)
E
9

Ok, finally found the correct answer. The cleanest way is to use a custom authentication backend.

# in settings:
AUTHENTICATION_BACKENDS = ('accounts.backends.AuthenticationBackend',)


# in accounts/backends.py:
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User


class AuthenticationBackend(ModelBackend):
    def get_user(self, user_id):
        try:
            # This is where the magic happens
            return User.objects. \
                select_related('profile'). \
                get(pk=user_id)
        except User.DoesNotExist:
            return None
Extinctive answered 13/6, 2013 at 13:27 Comment(0)
S
4

This is fairly ugly but you can probably monkeypatch the User objects property, eg. in a middleware:

# manager.py
from django.contrib.auth.models import UserManager

class MyUserManager(UserManager):
    def get_query_set(self):
        qs = super(MyUserManager, self).get_query_set()
        return qs.select_related('profile')

# middleware.py
from django.contrib.auth.middleware import AuthenticationMiddleware
from managers import MyUserManager

class MyAuthMiddleware(AuthenticationMiddleware):
    def process_request(self, request):
        super(AuthenticationMiddleware, self).process_request(request)
        User.objects = MyUserManager()
        return None

Then replace the line in settings.py:

MIDDLEWARE_CLASSES = (
    # ...
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    # ...
)

By:

# settings.py
MIDDLEWARE_CLASSES = (
    # ...
    'yourapp.middleware.MyAuthMiddleware',
    # ...
)

Note1: This code is purely theoric, never tested nor I have the time to.

Note2: I couldn't recommend using this solution from a long-term maintenability point of view.

Note3: If someone suggest something else, you should probably listen to him or her more than me.

Note4: As a probably better idea, why not trying to query for Profile, which is a model class you have total control on? You can always retrieve the user object from a profile anyway, so…

Sandbank answered 29/6, 2011 at 13:14 Comment(3)
Since I cannot find any cleaner way, I'm gonna accept this answer. However, I've filled a bug report in the django BT. Wait and see. code.djangoproject.com/ticket/16379Extinctive
You should really not accept this answer, because beginners could conclude that it's an acceptable approach for solving the problem you raise, while it's not.Sandbank
Tried and it didn't work alas... Would of been a simple workaround for meBrawn

© 2022 - 2024 — McMap. All rights reserved.