18

希望这一切都有意义:) 如有必要,我将通过评论进行澄清。另外,我正在尝试在这个问题中使用粗体文本,如果我(或你)发现它分散注意力,我会将其编辑掉。有了这个...

使用 django.contrib.auth 为我们提供了用户和组,以及其他我不能没有的有用的东西(比如基本消息传递)。

在我的应用程序中,我有几种不同类型的用户。一个用户只能是一种类型。这很容易由小组处理,但需要格外小心。但是,这些不同的用户在层次结构/关系中彼此相关。

让我们来看看这些用户:-

校长 - “顶级”用户

管理员 - 每个管理员向校长报告

协调员 - 每个协调员向管理员报告

除了这些之外,还有其他不直接相关的用户类型,但以后可能会相关。例如,“公司”是另一种类型的用户,可以有各种“产品”,产品可能由“协调者”监管。“买家”是另一种可能购买产品的用户。

现在所有这些用户都有各种其他属性,其中一些对所有类型的用户都是通用的,而另一些则只对一种用户类型不同。例如,所有类型的用户都必须有一个地址。另一方面,只有 Principal 用户属于“BranchOffice”。

上面提到的另一点是,用户只能是一种类型

该应用程序还需要跟踪谁创建和/或修改了委托人、管理员、协调员、公司、产品等。(这是用户模型的另外两个链接。)

在这种情况下,使用 Django 的多表继承是不是一个好主意,如下所示:-

from django.contrib.auth.models import User
class Principal(User):
    #
    #
    #    
    branchoffice = models.ForeignKey(BranchOffice)
    landline = models.CharField(blank=True, max_length=20)    
    mobile = models.CharField(blank=True, max_length=20)
    created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")    
    modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier")
    #
    #
    #

或者我应该这样做: -

class Principal(models.Model):
    #
    #
    #
    user = models.OneToOneField(User, blank=True)
    branchoffice = models.ForeignKey(BranchOffice)
    landline = models.CharField(blank=True, max_length=20)    
    mobile = models.CharField(blank=True, max_length=20)
    created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")    
    modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier")
    #
    #
    #

请记住,还有其他通过外键关联的用户类型,例如:-

class Administrator(models.Model):
    #
    #
    #
    principal = models.ForeignKey(Principal, help_text="The supervising principal for this Administrator")
    user = models.OneToOneField(User, blank=True)
    province = models.ForeignKey(         Province)
    landline = models.CharField(blank=True, max_length=20)    
    mobile = models.CharField(blank=True, max_length=20)
    created_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratorcreator")    
    modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratormodifier")

我知道 Django 确实在幕后使用一对一的关系进行多表继承。我只是没有足够的资格来决定哪种方法更合理。

4

5 回答 5

17

我想通过@thornomad 扩展解决方案。

直接扩展 Django 的 User 类会导致内部 django.auth 机制的各种麻烦。我在类似情况下所做的正是@thornomad 所建议的——我制作了自己的 UserProfile 模型与 Django User 模型一对一链接,在该模型中我保存了额外的用户数据,并从中继承了不同类型的模型的用户。

符合您描述的内容:

class UserProfile(models.Model):
    user = models.OneToOneField(User, blank=True, related_name='profile')
    class Meta:
        abstract = True


class PositionHolderUserProfile(UserProfile):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    landline = models.CharField(blank=True, max_length=20)    
    mobile = models.CharField(blank=True, max_length=20)
    created_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="created_users")    
    modified_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="modified_users")

class Principal(PositionHolderUserProfile):
    branchoffice = models.ForeignKey(BranchOffice)

class Administrator(PositionHolderUserProfile):
    superior = models.ForeignKey(Principal, related_name="subordinates")
    province = models.ForeignKey(Province)

class Coordinator(PositionHolderUserProfile):
    superior = models.ForeignKey(Administrator, related_name="subordinates")


class Company(UserProfile):
    name = models.CharField(max_length=50)

class Product(models.Model):
    name = models.CharField(max_length=50)
    produced_by = models.ForeignKey(Company)

class Buyer(UserProfile):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    products_bought = models.ManyToManyField(Product)
于 2009-12-05T13:07:04.527 回答
3

我最近切换到使用继承 contrib.auto.models.User 的模型。我的一般观察是,理论上它们很棒,但有时它们并没有像他们应该的那样被自动神奇地处理。

我认为您关于继承与 OneToOne 的决定归结为:

  • 我是否想让 Django 在 95% 的时间里自动做正确的事情,并且需要调试另外 5% 的时间

-或者-

  • 我想 100% 的时间自己手动做某事吗

如果您还没有看到它,Scott Barham 博客有一篇关于继承 User 以及构建自定义后端以确保返回您的自定义对象的精彩文章——扩展 Django 用户

另外感兴趣的是django-annoying提供的 AutoOneToOne 字段。它是这两种方法的混合——没有发生继承,但如果匹配的 OneToOneField 不存在,Django 会负责创建它。

此外,thornomad确实很好地说明了模型中的冗余。您可以轻松地实现一个抽象类来清理它(假设您正在手动执行 OneToOne):

class BaseExtendedUser(models.Model):
    user = models.OneToOneField(User, blank=True, related_name='profile')
    landline = models.CharField(blank=True, max_length=20)    
    mobile = models.CharField(blank=True, max_length=20)
    created_by = models.ForeignKey(User, editable=False, blank=True, related_name="created_users")    
    modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="modified_users")

    class Meta:
        abstract = True

class Administrator(BaseExtendedUser):
    province = models.ForeignKey(Province)

class Principal(BaseExtendedUser):
    branchoffice = models.ForeignKey(BranchOffice)
于 2009-11-25T16:25:51.997 回答
2

我不认为我会继承User模型,而是使用自定义-单独UserProfile留下模型。contrib.auth使用自定义UserProfile模型,您可以设置一个基本用户配置文件模型,该模型可以是您所有不同用户类型的一部分。

只是快速查看它,我会仔细查看任何重复所有相同字段的模型(例如您的最后两个PrincipleAdministrator模型)。将内置组功能与用户配置文件理念相结合可能会满足您的需求。

于 2009-11-25T12:36:48.610 回答
0

请考虑当 Coordinator 晋升为 Principal 时数据模型中会发生什么。在这种情况下,我根本不会使用继承。请重新考虑之前发帖者的建议“将内置组功能与用户配置文件理念相结合可能会满足您的需求。”

于 2009-11-29T11:53:05.747 回答
0

您是否需要用户类的对象在任何地方都像 auth.User 一样工作?这将是在 OneToOne 上使用继承的最明显原因。OneToOne 方法的一个优点是您可以轻松地切换到另一个用户模型,如果这是一个问题的话。

我在上面看到的真正问题(通过任何一种方法)是似乎没有任何东西阻止您让 Principal 对象和 Administrator 对象共享同一个用户。OneToOneField 只能保证任意两个关系之间的一对一映射。

于 2009-12-04T18:20:28.900 回答