The restricted usage of auth.User model when using custom AUTH_USER_MODEL

369 views
Skip to first unread message

Burak Emre Kabakcı

unread,
Feb 13, 2014, 12:06:57 PM2/13/14
to django-d...@googlegroups.com
I have two applications: One of them uses default auth.User model and the other one uses another AbstractUser class configured as AUTH_USER_MODEL. These two apps uses same codebase except their settings.py file. One of the models has a ForeignKey field in relation with auth.User model (I use auth.User as the staff model) and due to restriction of auth.User I'm not able to run the other application since I use custom AUTH_USER_MODEL. Django throws this error:

django.core.management.base.CommandError: One or more models did not validate:
model: 'staff' defines a relation with the model 'auth.User', which has been swapped out. Update the relation to point at settings.AUTH_USER_MODEL.

I couldn't get why the usage of User model is restricted in this case. The only workaround I can found is monkey-patching the swappable field in User model's Meta field.

Russell Keith-Magee

unread,
Feb 13, 2014, 5:45:16 PM2/13/14
to Django Developers
It's not clear if this question is better suited for django-users, or if you're asking with a view to contributing a patch to Django. For future reference, if this is a "How can I do this" question, it's better suited to Django-users. 

However, if you're considering options for "fixing" this: The reason the validation code does this is to ensure that the authentication code will work. 

At a conceptual data storage level, what you describe would be fine - admin users are normal auth.Users, regular users are myauth.MyUser. However, a user model isn't just used for data storage -- it's used for authentication and login, and Django's authentication framework can only handle one authentication model. AUTH_USER_MODEL defines which model that will be.

So - if the model validation code allowed for the situation you describe, you would have an admin user model, but no admin user would ever be able to log in. 

This isn't *necessarily* insurmountable; the auth backends tool would, in principle, allow authentication against different data sources; we'd just need to have a "I don't care what AUTH_USER_MODEL says, use auth.User" ModelBackend in addition to the actual ModelBackend that Django provides out of the box. You'd also need to make some changes to ensure that even though AUTH_USER_MODEL has been swapped out, you still want it to be synchronised - as currently set up, defining a value for AUTH_USER_MODEL other than auth.User causes auth.User to not be created on disk. 

However, I'm not convinced that this would be a worthwhile set of changes. If you go back to your original use case - why do you need two user models? Is there any reason that this can't be done the way Django does this right now - with a single user model that has a concept of "admin"? If 'system' users have additional data requirements, or you want to maintain a foreign-key level protection to ensure that admin users can't "own" data, then add a profile model to capture the idea of a 'system' user, and keep a foreign key between that user and the base User model. This approach separates the two concepts of authentication and authorisation -- the User model is used to identify that person X is who they say they are, the profile model is used to determine that person X is allowed to see particular content.

This approach has the added bonus that administrative users can be regular users in the system if the need arises without requiring a separate login to the system.

Alternatively, if you really do have a strong reason why you need to have two different types of user model, you could define your own version of auth.User -- using the abstract base classes provided, this is a 3 line definition:

    from django.contrib.auth.models import AbstractUser

    class User(AbstractUser):
        pass

and then duplicate the logic in django.contrib.auth.backends.ModelBackend to provide authentication against this model. Essentially this is duplicating all the auth.User functionality in your own set of "admin auth" models. 

Yours,
Russ Magee %-)



Burak Emre Kabakcı

unread,
Feb 27, 2014, 9:08:58 PM2/27/14
to django-d...@googlegroups.com
OK, I give up. Actually I asked another question related to this topic before developing the application but the answers were similar to yours. (https://groups.google.com/forum/#!topic/django-developers/EnMn9_hU5g0)

I had to use monkey-patching to allow dynamic AUTH_USER_MODEL but it wasn't a good idea so later I decided to separate the admin part and use different AUTH_USER_MODEL settings for each app. However, even separating the applications does not solve the problem because the core Django code does not assume that I can have different AUTH_USER_MODEL settings even if I use them in different server instances.

Actually the customer model and staff model don't share and fields except password, last_login and created_at but since I use OneToOneField in order to map extra fields it's ok to store all common type of data in one table.

I don't really like the idea of providing a regular customer user for staffs because when a staff logins into admin site, he/she also logins into the frontend site which makes harder to view the site as anonymous. Also it becomes much more difficult if I develop a feature "Login as x customer".

The only useful solution that I found is to move the admin page to another subdomain (with reverse proxies like nginx) in order to prevent them to share cookies. And I also created 2 proxy models called customer and staff and modified their managers so that they can act like separate models.
Reply all
Reply to author
Forward
0 new messages