Django:为什么要创建一个OneToOne到UserProfile而不是子类化auth.User?

时间:2011-03-27 20:44:21

标签: django django-models django-authentication django-users django-contrib

注意:如果你想通过告诉我你不喜欢django.contrib.auth来回答这个问题,请继续。这没有用。我很清楚这个问题的范围和意见。

现在,问题是:

惯例是使用OneToOne创建一个模型UserProfile。

在我能想到的各种方式中,一种更有效和更有效的方法是将User子类化为一个打算用于系统中每个人的类 - 一个名为Person(User)的类。

我没有看到为什么前者是传统的而后者被视为黑客的连贯解释。不久之前,我改用了OneToOne方法,以便获得使用get_profile()的能力,从此我就后悔了。我正在考虑转回去,除非我能够理解这种方法的优势。

3 个答案:

答案 0 :(得分:4)

你知道,不是吗,那个模型子类化是通过引擎盖下的OneToOne关系来实现的吗?事实上,就效率而言,我看不出这两种方法之间存在任何差异。

在我看来,现有混凝土模型的子类化是一个讨厌的黑客,如果可能的话应该避免。它涉及隐藏数据库关系,以便不清楚何时执行额外的数据库访问。明确地显示关系并在必要时明确地访问它们会更清楚。

现在,我喜欢的第三种选择是创建一个全新的User模型,以及一个自定义身份验证后端,它返回新模型的实例而不是默认模型的实例。创建后端只涉及定义几个简单的方法,因此很容易做到。

答案 1 :(得分:2)

至少在“官方”消息来源中,从来没有真正得到过很好的解释,至于为什么在实践中,子类化用户不如拥有UserProfile有用。

然而,我有几个原因,在我决定自己将子类化为“走的路”之后出现了。

  • 您需要自定义身份验证后端。这不是一个大问题,但是你需要编写的代码越少越好。
  • 其他应用可能假设您的用户是django.contrib.auth.models.User。除非该代码正在获取用户对象,否则这一切都会没问题。因为我们是一个子类,所以只使用我们的User对象的代码应该没问题。
  • 用户一次只能“成为”一个子类。例如,如果您拥有学生和教师的用户子类,那么在给定时间,您的用户将只能是教师或学生。使用UserProfiles,可以同时将同一用户连接的教师和学生配置文件。
  • 接下来,从一个子类转换到另一个子类很难:特别是如果你已经有一个子类的实例。

所以,你可能会说,“我的项目只会拥有一个用户子类”。那正是我所想。现在我们有三个,加上常规用户,可能还有第四个。 需求更改,不得不更改大量代码来处理这个问题并不是很有趣。

注意:最近有很多关于django-developers关于更好地解决与contrib.auth用户模型相关问题的讨论。

答案 2 :(得分:0)

继承User模型是否更有效率?我不明白为什么,但我想读你的论点。 IMNSHO,模型继承一直是一种痛苦。

然而,这可能无法解答您的问题,但我对Will Hardy在this snippet中提出的解决方案非常满意。通过利用信号,它会自动为每个新用户创建一个新的用户配置文件。

链接不太可能消失,但这是我的代码略有不同的版本:

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')

当然,任何功劳都归功于Will Hardy。