3

假设您必须对几个应该具有复合属性的类进行建模,例如尺寸宽度高度)或电话号码前缀号码扩展名)。

在 Java 中(使用 JPA 2)我会创建一个Dimensions类并用@Embeddable. 这会导致Dimension 的字段(例如widthheight)嵌入到每个声明类型为Dimensions属性的类中。

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

4

1 回答 1

5

我认为你可能过度思考继承。继承是并且实际上是组合模型的推荐方法。以下是如何在 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 Metawith 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__定义了如何构建类的实例的方式。更多关于这里。Django 使用这些来创建模型类实例。

所以要创建这个PhoneModelBase类,下面是一个粗略的大纲:

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

因此PhoneModelBase,即使它的定义看起来与常规模型非常相似,它也不是常规模型。它只是一个抽象类,旨在在其他模型中用作复合材料。

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

希望所有这些都有意义。如果没有,您只需要记住使用Metaclass with即可abstract = True

编辑

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

于 2012-09-19T12:20:50.157 回答