【问题标题】:Find object in child class from object in parent class in django从django的父类中的对象中查找子类中的对象
【发布时间】:2011-07-17 22:00:09
【问题描述】:

假设我有一个父类 (ThingsThatMigrate) 和两个子类 (Coconut 和 Swallow)。现在假设我有一个 ThingsThatMigrate 对象。我如何确定它实际上是椰子还是燕子?完成此操作后,我如何才能访问 Coconut 或 Swallow 对象?

【问题讨论】:

  • Coconut 和 Swallow 内部有什么区别?即一个有而另一个没有的任何额外字段或方法?
  • 额外的字段和方法以及进一步继承的不同分支

标签: django django-models


【解决方案1】:

Django 不提供这种开箱即用的模型多态性。最简单的方法是在其中存储新对象的内容类型。有一个名为 django-polymorphic-models 的简单通用应用程序,它为您提供此功能 - 并且 - 另外还有一个 downcast- 方法将返回子对象!

【讨论】:

  • 我对用户子类使用了类似的技术,并且效果很好。真希望我早知道这个应用程序存在。
  • 我曾经使用这种技术,直到我发现了更好的方法。 django-model-utils' InheritanceManager 不需要额外的 content-type 字段,并且由于内部 select_related 使用,可以在单个查询中获得异构结果集。
【解决方案2】:

具体的还是抽象的继承?如果具体:

>>> things = ThingsThatMigrate.objects.all().select_related('coconut', 'swallow')
>>> for thing in things:
...     thing = thing.coconut or thing.swallow or thing
...     print thing

这可以使用django-model-utils InheritanceManager 自动完成(然后您无需担心select_related 或手动列出所有可能的子类)。由另一个 Django 核心开发人员维护。

【讨论】:

  • 但这需要我知道我正在寻找椰子或燕子 - 我正在寻找一种不需要知道子类名称的解决方案。
  • 这就是为什么我将你指向InheritanceManager,并强调它如何避免列出子类:)
  • 我可以发布一个代码 sn-p 显示如何自省反向 OneToOne 关系以获取此信息,但您最好使用 InheritanceManager 或检查其源代码。
【解决方案3】:

它不是特别漂亮或高效,但我能想到的最好的方法是在你的ThingsThatMigrate 模型类中使用child() 方法而不将子类元数据存储在数据库中(就像django-polymorphic-models 一样) :

from django.core.exceptions import ObjectDoesNotExist

def child(self):
    for subclass in self.__class__.__subclasses__():
        try:
            return getattr(self, subclass.__name__.lower())
        except (AttributeError, ObjectDoesNotExist):
            continue

【讨论】:

  • 尽管取决于子类的声明位置以及导入语句的结构,__subclasses__ 可能不包含子类的完整列表。反省关系更安全。
【解决方案4】:

在我使用的 Django CMS (Merengue http://www.merengueproject.org/) 上,我们存储了“类名”属性,该属性存储了对象的真实类。

为了得到真实的实例,我们使用了以下方法:

    def get_real_instance(self):
        """ get object child instance """
        def get_subclasses(cls):
            subclasses = cls.__subclasses__()
            result = []
            for subclass in subclasses:
                if not subclass._meta.abstract:
                    result.append(subclass)
                else:
                    result += get_subclasses(subclass)
            return result

        if hasattr(self, '_real_instance'):  # try looking in our cache
            return self._real_instance
        subclasses = get_subclasses(self.__class__)
        if not subclasses:  # already real_instance
            self._real_instance = getattr(self, self.class_name, self)
            return self._real_instance
        else:
            subclasses_names = [cls.__name__.lower() for cls in subclasses]
            for subcls_name in subclasses_names:
                if hasattr(self, subcls_name):
                    return getattr(self, subcls_name, self).get_real_instance()
            return self

这个函数的重要之处在于它记住了类是否是抽象的,这会稍微改变逻辑。

【讨论】:

  • 还受到__subclasses__ 可能不完整的问题、额外字段的必要性和比 django-model-utils 更昂贵的查询的困扰。
  • 也许,django-model-utils 不能在多继承级别下正常工作。而且我真的不明白子类会出现什么问题
【解决方案5】:

正如 DrMeer 所建议的,我强烈推荐 django-model-utils(虽然现在托管在 bitbucket 上)。我不确定它是否足够令人信服。
让一个代码示例证明它:

>>> ThingsThatMigrate.objects.all().select_subclasses()
Coconut, Coconut, Swallow, Coconut, ThingsThatMigrate

它需要一行,objects = InheritanceManager() 在您的父模型中。

【讨论】:

    【解决方案6】:

    来自the docs

    如果您有一个Place 也是一个Restaurant,则可以使用小写版本的模型名称从Place 对象获取Restaurant 对象...

    【讨论】:

    • 但是如果正如我在问题中所问的那样,我有一个地点对象,但我不确定它是餐厅还是城堡?
    • 答案在同一个地方,在代码 sn-p 之后。
    • 我当然已经读过该文档很多次了。我没有看到您正在谈论的参考。您是在谈论一个明显的事实,即尝试与对象实例不同的孩子会引发该类的“DoesNotExist”?
    • 如果您访问 somemodel.coconut 并抛出异常,那么它显然不是椰子,因此必须是其他东西。
    • 是的,我明白了。文档清楚地说明了这一点。这就是为什么我提出了我所提出的问题,即——我怎样才能知道它是什么?我没有问我怎么知道它不是什么。你是说你认为我需要测试每个子类?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-01
    • 1970-01-01
    • 2021-01-25
    • 2011-06-29
    • 2011-04-17
    • 2023-03-14
    相关资源
    最近更新 更多