Django模型子类化方法

alf*_*ito 8 python django orm django-models

我设计一个新的Django应用程序,并且由于几种可能性,我不知道这将是最好的,所以我想的意见,并希望提高我走到这一步.

这个问题很接近,但并不完全.这个涉及平坦/嵌套的主题是有帮助的,但仍然没有回答问题.关于同一主题还有很多其他人,但没有人告诉我我想知道什么.

背景

模型具有一些共享属性的独特属性,我需要在另一个模型中引用它们,最好使用单个入口点,而不是为每个可能的模型都有一个字段.

我希望能够执行涉及Base类的复杂Django ORM查询,并在需要时按SubClass进行过滤.例如Event.objects.all(),返回所有事件.我知道Django模型utils继承管理器,并打算尽可能使用它.

此外,我将使用django admin来创建和管理对象,因此必须轻松集成.我希望能够直接创建一个新的SubEvent,而无需先创建一个Event实例.

为了说明,假设我有以下适用于app的模型A.

class Event(models.Model):
    commom_field = models.BooleanField()

    class Meta:
        abstract = True

class SubEventA(Event):
    email = models.EmailField(unique=True)

class SubEventB(Event):
    title = models.TextField()

class SubEventC(Event):
    number = models.IntegerField(default=10)

# and so on
Run Code Online (Sandbox Code Playgroud)

还有一个应用程序B,我希望能够引用任何类型的事件,例如:

class OtherModel(models.Model):
    event = models.ForeignKey('A.Event')

# This won't work, because `A.Event` is abstract.
Run Code Online (Sandbox Code Playgroud)

可能的解决方案

  1. 使用GenericForeignKey.

    # B.models.py
    class OtherModel(models.Model):
        content_type = models.ForeignKey(ContentType)
        object_id = models.PositiveIntegerField()
        event = GenericForeignKey('content_type', 'object_id')
    
    Run Code Online (Sandbox Code Playgroud)

    我不喜欢这个是我将失去Django ORM的查询功能,我可能需要做额外的小工作才能让它在管理上工作.不确定,以前从未处理过这个问题

  2. 弄平 Event

    我可以把它全部带到基类,并在模型定义之外有标志或检查,例如:

    class Event(models.Model):
        commom_field = models.BooleanField()
        email = models.EmailField(blank=True)
        title = models.TextField(blank=True)
        number = models.IntegerField(default=10)
    
    Run Code Online (Sandbox Code Playgroud)

    这看起来似乎是最好的想法,但当然还有其他类型的字段,这迫使我允许大多数字段(如电子邮件字段)的空值/空格,丢失数据库级别完整性检查.

  3. OneToOne关系

    而不是像1上的抽象或2上的展平,每个都可以有一个db表,模型看起来像:

    class Event(models.Model):
        commom_field = models.BooleanField()
    
    class SubEventA(models.Model):
        event = models.OneToOneField(Event)
        email = models.EmailField(unique=True)
    
    class SubEventB(models.Model):
        event = models.OneToOneField(Event)
        title = models.TextField(blank=True)
    
    class SubEventC(models.Model):
        event = models.OneToOneField(Event)
        number = models.IntegerField(default=10)
    
    Run Code Online (Sandbox Code Playgroud)

    到目前为止,它解决了两个初始问题,但现在当我进入管理界面时,我必须Event在保存SubEvent实例之前自定义每个表单以创建基础.

问题

  1. 有更好的方法吗?

  2. 我提出的任何选择都可以在任何方向上进行改进(ORM查询,数据库约束,管理界面)吗?

alf*_*ito 9

我对这两个答案都进行了思考,并根据这些建议提出了一些建议。因此,我正在添加我自己的这个答案。

我选择使用django-polymorphic,这是@professorDante 建议的相当不错的工具。由于这是一个多表继承,@albar 的回答也有些正确。

tl;博士

django-polymorphic满足 3 个主要要求:

  1. 允许 Django ORM 查询样式
  2. 通过多表继承和每个子类一个表来保持数据库级别的约束
  3. 简单的 Django 管理集成

更长的版本

Django-polymorphic 允许我从基类查询所有不同的事件实例,例如:

# assuming the objects where previously created
>>> Event.objects.all()
[<SubEventA object>, <SubEventB object>, <SubEventC object>]
Run Code Online (Sandbox Code Playgroud)

它还具有出色的django 管理集成,允许无缝对象创建和编辑。

使用 django-polymorphic 的模型如下所示:

# A.models.py
from polymorphic import PolymorphicModel

class Event(PolymorphicModel):
    commom_field = models.BooleanField()

    # no longer abstract

class SubEventA(Event):
    email = models.EmailField(unique=True)

class SubEventB(Event):
    title = models.TextField()

class SubEventC(Event):
    number = models.IntegerField(default=10)



# B.models.py

# it doesnt have to be polymorphic to reference polymorphic models
class OtherModel(models.Model):
    event = models.ForeignKey('A.Event')
Run Code Online (Sandbox Code Playgroud)

此外,我只能从另一个类引用基本模型,我可以直接分配任何子类,例如:

>>> sub_event_b = SubEventB.objects.create(title='what a lovely day')
>>> other_model = OtherModel()
>>> other_model.event = sub_event_b
Run Code Online (Sandbox Code Playgroud)