Django模型子类化:通过查询父类获取子类

10 投票
4 回答
15158 浏览
提问于 2025-04-16 00:22

下面是给出的代码:

class BaseMedium(models.Model):
    title = models.CharField(max_length=40)
    slug = models.SlugField()

class A(BaseMedium):
    url = models.URLField()

class B(BaseMedium):
    email = models.EmailField()

我现在想查询每一个BaseMedium。

b = BaseMedium.objects.all()

我该如何打印出所有信息,包括子类的字段,而不需要知道子类的具体类型呢?

如果b[0]实际上是一个A实例,b[0].a会打印出相关信息;但如果它是B的实例,就会出现DoesNotExist的错误。

这很合理,但我希望有一个通用的变量或方法,可以返回相关的对象。

也许我的数据库设计不太适合这样查询,如果是这样的话,我很乐意听听你们的建议,看看有没有更好的设计。

我考虑过使用GenericForeignKey

class Generic(models.Model):
    basemedium = models.ForeignKey('BaseMedium')
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    object = generic.GenericForeignKey('content_type', 'object_id')

但这个解决方案似乎太复杂了,我觉得你们可能有更好的办法。

4 个回答

2

感谢Roseman先生的回复。我对您的想法进行了进一步的开发。以下是我想到的内容:

def related_object(self, default_pointer_name='_ptr'):
        models = [A,B] #models
        object = None

        argument = '%s%s' %(self.__class__.__name__.lower(), default_pointer_name)
        query = { argument : self}

        for model in models:
            try:
                object = model.objects.get(**query)
            except model.DoesNotExist:
                pass
            else:
                return object

        if object == None:
            raise RelatedObjectException
        return object

这是BaseMedium使用的一种方法。

2

要做到这一点,唯一的方法就是在基础模型上明确存储它是什么类型。所以在BaseMedium里加一个derived_type(或者其他你喜欢的名字)字段,并在保存的时候设置这个字段。这样你就可以有一个get_derived_type的方法:

def get_derived_type(self):
    if self.derived_type ==  'A':
        return self.a
    elif self.derived_type == 'B':
        return self.b

然后就可以继续了。

6

你可以看看Carl Meyer之前发布的解决方案。这个方法内部使用了ContentType的方式,但它的封装做得非常优雅。

他还提到了一种替代方案,这种方案更高效,不需要在数据库中额外存储一个字段,但它只适用于直接的子类。如果你有多个继承层级,第一种解决方案会更好。

撰写回答