10

注意:如果你想通过告诉我你不喜欢 django.contrib.auth 来“回答”这个问题,请继续。那不会有帮助。我很清楚在这个问题上意见的范围和强度。

现在,问题:

约定是创建一个模型,UserProfile,对用户具有 OneToOne。

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

我还没有看到一个连贯的解释,为什么前者是传统的,而后者被认为是一种黑客行为。前段时间,为了获得使用 get_profile() 的能力,我改用 OneToOne 方法,从那以后我就后悔了。我正在考虑切换回去,除非我可以理解这种方法的优势。

4

3 回答 3

4

您是否意识到模型子类化是通过 OneToOne 关系实现的?事实上,就效率而言,我看不出这两种方法有什么区别。

在我看来,现有具体模型的子类化是一种令人讨厌的 hack,应该尽可能避免。它涉及隐藏数据库关系,以便不清楚何时执行额外的数据库访问。显式显示关系并在必要时显式访问它们会更加清晰。

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

于 2011-03-27T20:56:54.410 回答
2

从来没有一个很好的解释,至少从“官方”来源来看,为什么在实践中子类化 User 不如拥有 UserProfile 有用。

但是,我有几个原因,这是在我自己决定子类化 User 是“要走的路”之后出现的。

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

因此,您可能会说,“我的项目将永远只有一个 User 子类”。我也那么认为。现在我们有三个,加上普通用户,可能还有第四个。需求发生变化,不得不更改大量代码来处理这种情况并不是很有趣。

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

于 2012-07-10T03:51:59.207 回答
0

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

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

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

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。

于 2011-03-27T21:37:25.447 回答