【问题标题】:Django model subclassing: Get the subclass by querying the superclassDjango模型子类化:通过查询超类获取子类
【发布时间】:2010-06-24 11:23:17
【问题描述】:

给出以下代码:

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')

但是这个解决方案似乎很复杂,我认为你们有更好的解决方案。

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    谢谢先生。罗斯曼为您的答复。 我进一步发展了你的想法。 这是我想出的:

    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 使用的方法。

    【讨论】:

    • 你应该小心这个。如果您要从数据库中检索 1000 条记录,这将在一个 SELECT 中完成,但随后将需要 1000 次单选来获取每个 BaseMedium 对象的子类型。无论如何,我不知道如何做到这一点。我使用类似的东西,它会产生很多额外的数据库查询......
    • 拍摄你是对的。自定义 sql 可能是一个选项......奇怪的是我不明白这不是已知解决方案的常见问题。
    【解决方案2】:

    这对我有用(使用 self.subclass_name_in_lower_case): 在这个例子中,子类是TextTreeItem, CategoryTreeItem and KeywordTreeItem

    class TreeItem(MPTTModel):
        parent = TreeForeignKey('self', on_delete=models.CASCADE, verbose_name=_('Parent'),
                                null=True, blank=True, related_name='%(class)s_related')
    
        objects = CustomTreeManager()
    
        @property
        def daughter(self):
            try:
                return self.texttreeitem
            except TreeItem.texttreeitem.RelatedObjectDoesNotExist:
                pass
    
            try:
                return self.categorytreeitem
            except TreeItem.categorytreeitem.RelatedObjectDoesNotExist:
                pass
    
            try:
                return self.keywordtreeitem
            except TreeItem.keywordtreeitem.RelatedObjectDoesNotExist:
                return self
    

    【讨论】:

      【解决方案3】:

      您应该检查一下解决方案posted by Carl Meyer 前一段时间。它在内部使用了 ContentType 方法,但它封装得非常优雅。

      他还指出了另一种更有效的解决方案,它不需要在数据库中存储额外的字段,但它只适用于直接子类。如果您有多个继承级别,则第一个解决方案更好。

      【讨论】:

        【解决方案4】:

        这样做的唯一方法是在基本模型上显式存储它是什么类型。所以在 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
        

        等等。

        【讨论】:

          猜你喜欢
          • 2014-11-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-25
          • 1970-01-01
          • 1970-01-01
          • 2016-01-11
          相关资源
          最近更新 更多