Django模型子类化:通过查询父类获取子类
下面是给出的代码:
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的方式,但它的封装做得非常优雅。
他还提到了一种替代方案,这种方案更高效,不需要在数据库中额外存储一个字段,但它只适用于直接的子类。如果你有多个继承层级,第一种解决方案会更好。