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

Django多表继承与在模型中指定显式OneToOne关系,django,inheritance,multi-table,Django,Inheritance,Multi Table,希望所有这些都有意义:)如果有必要,我会通过评论澄清。另外,我正在试验在这个问题中使用粗体文本,如果我(或你)发现它分散注意力,我会将其编辑掉。有了它的方式 使用django.contrib.auth可以为我们提供用户和组,以及其他我无法缺少的有用功能(如基本消息) 在我的应用程序中,我有几种不同类型的用户。用户只能是一种类型。这将很容易由小组来处理,只需额外小心一点但是,这些不同的用户在层次结构/关系中相互关联。 让我们看看这些用户:- 负责人-“顶级”用户 管理员-每个管理员向负责人报告 协

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

使用django.contrib.auth可以为我们提供用户和组,以及其他我无法缺少的有用功能(如基本消息)

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

让我们看看这些用户:-

负责人-“顶级”用户

管理员-每个管理员向负责人报告

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

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

现在,所有这些用户都具有各种其他属性,其中一些属性对所有类型的用户都是通用的,而其中一些属性仅对一种用户类型不同。例如,所有类型的用户都必须有一个地址。另一方面,只有主要用户属于“分支机构”

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

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

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

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


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

我最近转而使用继承contrib.auto.models.User的模型。我的总体观察结果是,理论上它们很棒,但有时它们并没有像人们想象的那样得到自动神奇的处理

我认为你关于继承权与OneToOne的决定可以归结为:

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

  • 我是否想在100%的时间里自己手动做一些事情
如果您还没有看到,Scott Barham的博客中有一篇关于继承用户的内容的文章,还介绍了如何构建自定义后端以确保您的自定义对象被返回--

此外,还需要注意由提供的AutoOneTONE字段。这有点像是两种方法的混合——没有发生继承,但是Django负责创建匹配的OneToOneField,如果它不存在的话

此外,对于模型中的冗余也有很好的说明。您可以很容易地实现一个抽象类来清理它(假设您正在手动执行OneToOne):


请考虑当协调器被提升为主体时数据模型中发生的情况。在这种情况下,我根本不会使用继承。请重新考虑上一张海报的建议“将内置组功能与用户配置文件理念相结合可能会满足您的需求。”

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


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

我想通过@thornomad进一步介绍解决方案

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

符合您描述的内容:

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)

还有其他类型的用户,我真的很想分开。当我看我的模型时,我希望看到负责人、管理员、协调员是相关的,但公司和买方与这些都没有直接关系。我只是想澄清一下,我确实打算在有益的地方使用抽象模型。此外,我还打算为不同类型的用户提供不同的模型。>但有时它们不会像预期的那样自动处理。您能否对此进行详细说明,并让我们知道他们的行为与exp不符
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)