Django: Why create a OneToOne to UserProfile instead of subclassing auth.User?

删除回忆录丶 提交于 2019-11-27 20:50:20

You do realise, don't you, that model subclassing is implemented by means of a OneToOne relationship under the hood? In fact, as far as efficiency is concerned, I cannot see any difference at all between these two methods.

Subclassing of existing concrete models is, in my opinion, a nasty hack that should be avoided if at all possible. It involves hiding a database relationship so that it is unclear when extra db access is performed. It's much clearer to show the relationships explicitly, and access them explicitly where necessary.

Now, a third alternative which I do like is to create a completely new User model, along with a custom authentication backend that returns instances of the new model instead of the default one. Creating a backend only involves defining a couple of simple methods, so it's very easy to do.

There's never really been a good explanation, at least from "official" sources as to why, in practice, subclassing User is less useful than having a UserProfile.

However, I have a couple of reasons, that came up after I had decided myself that subclassing User was "the way to go".

  • You need a custom authentication backend. This is not a big issue, but the less code you need to write, the better.
  • Other apps may be assuming that your User is a django.contrib.auth.models.User. Mostly this will be okay, unless that code is fetching User objects. Because we are a subclass, any code just using our User objects should be fine.
  • A User may only 'be' one sub-class at a time. For instance, if you had User subclasses of Student and Teacher, then at a given time, your User would only be able to be a Teacher or a Student. With UserProfiles, there could be both a Teacher and a Student profile attached to the same user at the same time.
  • Following on, converting from one sub-class to another is hard: especially if you have an instance of one sub-class already.

So, you may say, "my project will only ever have the one User subclass". That's what I thought. Now we have three, plus regular Users, and possibly a fourth. Requirements change, having to change heaps of code to deal with that is not much fun.

note: There has been quite a lot of discussion on django-developers recently about a better fix to the issues related to the contrib.auth User model.

Is it more efficient and effective to inherit the User model? I don't see why, but I'd like to read your arguments. IMNSHO, model inheritance has always been a pain.

Yet, this may not answer your question, but I'm quite satisfied with the solution proposed by Will Hardy in this snippet. By taking advantage of signals, it automatically creates a new user profile for every new user.

The link is unlikely to disappear, but here's my slightly different version of his code:

from django.contrib.auth.models import User
from django.db import models
from django.db.models.signals import post_save
from django.utils.translation import ugettext_lazy as _

class AuthUserProfileModelBase(models.base.ModelBase):
    # _prepare is not part of the public API and may change
    def _prepare(self):
        super(AuthUserProfileModelBase, self)._prepare()
        def on_save(sender, instance, created, **kwargs):
            if created:
                self.objects.create(user=instance)
        # Automatically link profile when a new user is created
        post_save.connect(on_save, sender=User, weak=False)

# Every profile model must inherit this class
class AuthUserProfileModel(models.Model):
    class Meta:
        abstract = True
    __metaclass__ = AuthUserProfileModelBase
    user = models.OneToOneField(User, db_column='auth_user_id',
        primary_key=True, parent_link=True)

# The actual profile model
class Profile(AuthUserProfileModel):
    class Meta:
        app_label = 'some_app_label'
        db_table = 'auth_user_profile'
        managed = True
    language = models.CharField(_('language'), max_length=5, default='en')

Of course, any credit goes to Will Hardy.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!