如何在 Django 中查询基于抽象类的对象?

问题描述 投票:0回答:7

假设我有一个如下所示的抽象基类:

class StellarObject(BaseModel):
  title = models.CharField(max_length=255)
  description = models.TextField()
  slug = models.SlugField(blank=True, null=True)

  class Meta:
    abstract = True

现在,假设我有两个继承自 StellarObject 的实际数据库类

class Planet(StellarObject):
  type = models.CharField(max_length=50)
  size = models.IntegerField(max_length=10)

class Star(StellarObject):
  mass = models.IntegerField(max_length=10)

到目前为止,一切都很好。如果我想获得行星或恒星,我要做的就是:

Thing.objects.all() #or
Thing.objects.filter() #or count(), etc...

但是如果我想获得所有 StellarObjects 该怎么办?如果我这样做:

StellarObject.objects.all()

它当然会返回错误,因为抽象类不是实际的数据库对象,因此无法查询。我读过的所有内容都说我需要执行两个查询,每个查询针对行星和恒星,然后合并它们。这看起来效率极低。这是唯一的方法吗?

django abstract-class
7个回答
51
投票

从根本上来说,这是对象和关系数据库之间不匹配的一部分。 ORM 在抽象差异方面做得很好,但有时你还是会遇到它们。

基本上,您必须在抽象继承(在这种情况下两个类之间没有数据库关系)或多表继承(在每次查询时以牺牲效率为代价(额外的数据库联接)保持数据库关系)之间进行选择。


14
投票

您无法查询抽象基类。对于多表继承,您可以使用 django-model-utils ,它是

InheritanceManager
,它使用
QuerySet
方法扩展了标准
select_subclasses()
,它可以满足您的需要:它左连接所有继承的表并返回适当的值为每行键入实例。


10
投票

这是模型中多态性的一个示例(多态性 - 一种的多种形式)。

选项 1 - 如果您只有一个地方处理此问题:

为了在一两个地方使用一点 if-else 代码,只需手动处理它 - 在开发/维护方面可能会更快、更清晰(即,除非这些查询是认真的,否则可能值得)敲击你的数据库 - 这是你的判断,取决于具体情况)。

选项 2 - 如果您经常这样做,或者确实需要优雅的查询语法:

幸运的是,django 中有一个处理多态性的库,django-polymorphic - 这些文档将向您展示如何精确地做到这一点。正如您所描述的,这可能是直接查询的“正确答案”,特别是如果您想在很多地方进行模型继承。

选项 3 - 如果您想要中途之家:

这种方法具有上述两种方法的缺点,但我过去已经成功地使用它来自动将多个查询集压缩在一起,同时保留一个查询集对象包含两种类型模型的优点.

查看 django-querysetsequence,它管理多个查询集的合并。

它不像

django-polymorphic
那样得到很好的支持或稳定,但仍然值得一提。


9
投票

如果需要在基类上进行查询,请不要使用抽象基类。请改用具体的基类。


1
投票

这种情况我想也没有其他办法了。

为了优化,您可以避免从抽象

StellarObject
继承,并将其用作通过 FK 连接到
Star
Planet
对象的单独表。

这样他们俩都会有 ie。

star.stellar_info.description

另一种方法是添加额外的模型来处理信息并使用 StellarObject 作为多对多关系中的

through


1
投票

如果您希望根据各自的子类将不同的子类行为与对象联系起来,我会考虑放弃抽象继承模式或具体基本模式。

当你通过父类查询时——这听起来像是你想要做的——Django将结果对象视为父类的对象,因此访问子类级别的方法需要将对象重新转换为它们的“正确的” ' 动态子类,以便他们可以看到这些方法......此时,挂在父类级别方法上的一系列 if 语句可以说是一种更干净的方法。

如果上述子类行为不是问题,您可以考虑将自定义管理器附加到抽象基类,通过原始 SQL 将模型缝合在一起。

如果您主要对将一组离散的相同数据字段分配给一堆对象感兴趣,我会沿着外键进行关联,就像 bx2 所建议的那样。


0
投票

这看起来效率极低。这是唯一的方法吗?

据我所知,这是 Django ORM 的唯一方法。目前实现的抽象类是将类的公共属性抽象为超类的便捷机制。 ORM 不提供类似的查询抽象。

您最好使用另一种机制在数据库中实现层次结构。实现此目的的一种方法是使用单个表并使用类型“标记”行。或者,您可以为另一个保存属性的模型实现通用外键(后者甚至对我来说听起来也不正确)。

© www.soinside.com 2019 - 2024. All rights reserved.