使用Django多重继承确定子类类型还是将类型指定为字段?

2 投票
2 回答
887 浏览
提问于 2025-04-16 05:52

我正在使用Django设置一个数据模型,采用多表继承(MTI),像这样:

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的环境中工作。

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

2 个回答

-1

我真的需要这样做吗?

绝对不需要。绝对不需要。绝对不需要。

Django有没有什么方法可以自动处理这个问题?

有的,这个方法叫“多态”。

永远不需要知道子类是什么。绝对不需要。

“那我的WebMetric.url和TextMetric.text属性怎么办?”

打算用这些属性做什么呢?定义一个方法,去一些事情。在WebMetric中实现一个版本(使用url),在TextMetric中实现另一个版本(使用text)。这就是正确的多态。


请阅读这个: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

你想要的是“单表继承”。

1

虽然这可能会让一些人感到不舒服,但解决这个问题的唯一实际方法是,在基类中放一个字段或方法,来说明每条记录到底是什么类型的对象。你提到的方法的问题在于,它需要对每种子类进行单独的数据库查询,这样对于每个你处理的对象来说,效率会非常低下,尤其是在处理大量数据时会变得非常慢。更好的方法是使用外键(ForeignKey)来关联Django的内容类型类。

@Carl Meyer在这里提供了一个不错的解决方案:如何在Django中访问对象的子类,而不需要知道子类的名称?

单表继承(Single Table Inheritance)可能会帮助缓解这个问题,具体取决于它的实现方式。但目前Django并不支持这个功能:Django中的单表继承,所以这个建议并不太有用。

撰写回答