【问题标题】:Figure out child type with Django MTI or specify type as field?使用 Django MTI 找出子类型或将类型指定为字段?
【发布时间】:2026-01-02 15:40:02
【问题描述】:

我正在使用多表继承 (MTI) 在 django 中设置数据模型,如下所示:

class Metric(models.Model):
    account = models.ForeignKey(Account)
    date = models.DateField()
    value = models.FloatField()
    calculation_in_progress = models.BooleanField()
    type = models.CharField( max_length=20, choices= METRIC_TYPES ) # Appropriate?

    def calculate(self):
        # default calculation...

class WebMetric(Metric):
    url = models.URLField()

    def calculate(self):
        # web-specific calculation...

class TextMetric(Metric):
    text = models.TextField()

    def calculate(self):
        # text-specific calculation...

我的直觉是在基类中放置一个“类型”字段,如此处所示,这样我就可以知道任何 Metric 对象属于哪个子类。一直保持最新状态会有点麻烦,但有可能。但是我需要这样做吗? django 有没有办法自动处理这个问题?

当我调用Metric.objects.all() 时,返回的每个对象都是Metric 的实例,而不是子类。所以如果我打电话给.calculate(),我永远不会得到子类的行为。

我可以在基类上编写一个函数来测试我是否可以将其转换为任何子类型,例如:

def determine_subtype(self):
    try:
        self.webmetric
        return WebMetric
    except WebMetric.DoesNotExist:
        pass
    # Repeat for every sub-class

但这似乎是一堆重复的代码。而且它也不能包含在 SELECT 过滤器中——只能在 python 空间中使用。

处理这个问题的最佳方法是什么?

【问题讨论】:

  • 我不确定我是否理解您的问题...您的意思是说您希望在执行某些操作时能够识别“Metric”是“WebMetric”还是“TextMetric”在'Metric'上,例如:'a_metric_to_recognize = Metric.objects.get(pk=some_pk)' ???

标签: python django django-models


【解决方案1】:

但是我需要这样做吗?

从来没有。绝不。从不。

django 有没有办法自动处理这个问题?

是的。这就是所谓的“多态性”。

从不需要知道子类。从不。

“我的 WebMetric.url 和 TextMetric.text 属性呢?”

你会用这些属性做什么?定义一个某事的方法函数。在 WebMetric(使用 url)和 TextMetric(使用文本)中实现不同的版本。 这就是正确的多态性。


请阅读:http://docs.djangoproject.com/en/1.2/topics/db/models/#abstract-base-classes

请把你的超类抽象化。

不要这样做:http://docs.djangoproject.com/en/1.2/topics/db/models/#multi-table-inheritance

你想要“单表继承”。

【讨论】:

  • 我认为这行不通。如果我调用 Metric.objects.all() 它实际上会返回子类对象吗?我认为它只会返回 Metric 对象。请参阅docs.djangoproject.com/en/dev/topics/db/models/… 中的第三个代码 sn-p,他们将对象显式转换为其子类。
  • 我认为 S. Lott 的意思是您不应该在“Metric”上做任何特定于“WebMetric”或“TextMetric”的事情。在“Metric”上定义您需要的所有函数,在子类中覆盖它们,然后在“Metric”上调用它们。如果您需要明确知道对象来自哪个子类,您可能会遇到设计问题。操纵“度量”,因此只操纵通用功能或子类之一。请更详细地描述您要做什么,然后我们可以帮助您进行设计!
  • S.Lott 的想法在理论上很好,但它在 Django 中不起作用。我刚检查过。当我使用 Metric.objects 从数据库中提取对象时,它们会作为 Metric 对象返回,而不是作为子类的实例。所以多态在这里实际上不起作用。如果我在基类中定义一个方法并在子类中覆盖它,子类的方法将不会被调用,因为我实际上没有子类的实例。
  • 避免“多表继承”。阅读:docs.djangoproject.com/en/1.2/topics/db/models/…。让你的超类抽象。
  • 我为什么要避免 MTI?因为多态不起作用?这就是我正在寻找的答案。抽象基类是不合适的,因为我经常想要没有额外数据的基类。抽象基类在从数据库中提取对象方面是相同的——我仍然需要知道它们是什么类才能执行正确的查询来找到它们。
【解决方案2】:

虽然它可能会冒犯某些人的感受,但解决此问题的唯一实用方法是在基类中放置一个字段或一个方法,以说明每条记录真正是哪种对象。您描述的方法的问题在于,对于您正在处理的每个对象,它需要对每种类型的子类进行单独的数据库查询。处理大型查询集时,这可能会变得非常慢。更好的方法是对 django Content Type 类使用 ForeignKey。

@Carl Meyer 在这里写了一个很好的解决方案:How do I access the child classes of an object in django without knowing the name of the child class?

单表继承可以帮助缓解这个问题,具体取决于它的实现方式。但目前 Django 不支持它:Single Table Inheritance in Django 所以这不是一个有用的建议。

【讨论】:

  • 谢谢!使用 ContentType 既好又通用。