Django多表继承VS在模型中指定显式的OneToOne关系

时间:2009-11-25 12:18:21

标签: django inheritance multi-table

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

使用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确实在幕后使用一对一的关系进行多表继承。我没有足够的资格来决定哪种方法更合理。

5 个答案:

答案 0 :(得分:14)

我想通过@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)

答案 1 :(得分:3)

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

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

  • 我是否希望让Django在95%的时间内自动执行某项操作,并且需要调试其他5%

-OR -

  • 我是否想在100%的时间内自己动手

如果您还没有看到它,那么Scott Barham博客有一篇关于继承User的好文章,还有一个自定义后端来确保您的自定义对象被返回 - Extending the Django User

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

此外,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)

答案 2 :(得分:2)

我认为我不会继承User模型,而是使用自定义UserProfile - 仅保留contrib.auth模型。使用自定义UserProfile模型,您可以设置基本用户个人资料模型,该模型可以是所有不同用户类型的一部分。

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

答案 3 :(得分:0)

请考虑协调员升级为校长时数据模型中发生的情况。在这种情况下我根本不会使用继承。请重新考虑上一张海报的建议“将内置的群组功能与用户个人资料构思结合起来,可以满足您的需求。”

答案 4 :(得分:0)

您是否需要用户类的对象在任何地方充当auth.User?这是在OneToOne上使用继承的最明显的原因。 OneToOne方法的一个专业人员将是您可以轻松切换到另一个用户模型,如果这是一个问题。

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