Django:复合字段或嵌入类(如JPA)?

时间:2012-09-19 11:12:42

标签: python django django-models

假设您必须为几个应具有复合属性的类建模,例如维度宽度高度)或电话号码< / em>(前缀数字扩展名)。

在Java中(使用JPA 2)我将创建一个 Dimensions 类,并使用@Embeddable对其进行注释。这会导致 Dimension的字段(例如 width height )嵌入到声明属性为 Dimensions

如何使用Django对这些进行建模,同时避免代码重复?创建单独的维度模型并使用ForeignKey字段引用它是没有意义的。并且这些类没有足够的共同点来证明模型继承的合理性。

1 个答案:

答案 0 :(得分:4)

我认为你可能会过度思考继承。继承是并且实际上是复合模型的推荐方法。以下是如何在Django中正确使用模型继承的示例:

class PhoneModelBase(model.Model):
    phone = models.CharField(max_length=16)
    ...

    class Meta:
        abstract = True

class PhoneModel(PhoneModelBase):
    # phone is here without typing it
    # the only restriction is that you cannot redefine phone here
    # its a Django restriction, not Python restriction
    # phone = models.CharField(max_length=12) # <= This will raise exception
    pass

这样做会创建一个模型PhoneModelBase,但这里的关键是它使用class Meta abstract=True

这里有更多关于正在发生的事情和一些Python概念的解释。我假设你没有意识到它们,因为你在问题中提到了Java。这些Python概念实际上是相当混乱的概念,所以我的解释可能不完整,甚至令人困惑,所以如果你不遵循,不要介意。您只需要使用abstact = True即可知道。以下是官方文档:https://docs.djangoproject.com/en/dev/topics/db/models/#abstract-base-classes

Meta中的

PhoneModelBase属性就是属性。它与类中的任何其他属性相同,除了它是一个类实例(请记住,在Python类和函数中是一阶成员)。另外,Python有一个名为__metaclass__的东西,你可以添加到你的类中。 __metaclass__定义了如何构建类实例的方法。更多关于here的信息。 Django在创建模型类实例的过程中使用了这些。

因此,要创建PhoneModelBase类,以下是粗略概述:

  • 当创建PhoneModelBase类的实例(类本身,而不是类的实例 - PhoneModelBase())时,来自__metaclass__的{​​{1}}由于继承接管创建过程
  • model.Model中,Python调用创建实际类实例的函数,并将您尝试创建的类中的所有字段传递给它 - PhoneModelBase。这将包括__metaclass__phone以及您定义的任何其他字段
  • 它会看到Meta属性,然后开始分析其属性。根据这些属性的值,Django将改变模型的行为
  • 它会看到Meta属性,然后通过不将其存储在db
  • 中来更改其尝试创建的类的逻辑 - abstract

那么PhoneModelBase,即使它的定义看起来非常类似于常规模型,它也不是常规模型。它只是一个抽象类,可以在其他模型中用作复合类。

当其他模型继承自PhoneModelBase时,他们的PhoneModelBase将复制基本模型中的属性,就像您手动键入这些属性一样。对于这样的事情,它不会是外键。所有继承的属性都将成为模型的一部分,并且将位于同一个表中。

希望所有这一切都有道理。如果没有,您只需将__metaclass__类与Meta一起使用即可。

修改

正如评论中所建议的那样,您也可以从多个基类继承。因此,您可以拥有abstract = TruePhoneModelBase,然后您可以继承这两个(或更多),并且所有基类的所有属性都将出现在您的模型中。