如何在 Django 中查询基于抽象类的对象?
假设我有一个如下所示的抽象基类:
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()
它当然会返回错误,因为抽象类不是实际的数据库对象,因此无法查询。我读过的所有内容都说我需要执行两个查询,每个查询针对行星和恒星,然后合并它们。这看起来效率极低。这是唯一的方法吗?
Let's say I have an abstract base class that looks like this:
class StellarObject(BaseModel):
title = models.CharField(max_length=255)
description = models.TextField()
slug = models.SlugField(blank=True, null=True)
class Meta:
abstract = True
Now, let's say I have two actual database classes that inherit from 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)
So far, so good. If I want to get Planets or Stars, all I do is this:
Thing.objects.all() #or
Thing.objects.filter() #or count(), etc...
But what if I want to get ALL StellarObjects? If I do:
StellarObject.objects.all()
It of course returns an error, because an abstract class isn't an actual database object, and therefore cannot be queried. Everything I've read says I need to do two queries, one each on Planets and Stars, and then merge them. That seems horribly inefficient. Is that the only way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
从根本上来说,这是对象和关系数据库之间不匹配的一部分。 ORM 在抽象差异方面做得很好,但有时你还是会遇到它们。
基本上,您必须在抽象继承(在这种情况下两个类之间没有数据库关系)或多表继承(在每个查询中以牺牲效率为代价(额外的数据库联接)保持数据库关系)之间进行选择。
At its root, this is part of the mismatch between objects and relational databases. The ORM does a great job in abstracting out the differences, but sometimes you just come up against them anyway.
Basically, you have to choose between abstract inheritance, in which case there is no database relationship between the two classes, or multi-table inheritance, which keeps the database relationship at a cost of efficiency (an extra database join) for each query.
您无法查询抽象基类。对于多表继承,您可以使用 django-model-utils ,它是
InheritanceManager
,它使用select_subclasses()
方法扩展标准QuerySet
,该方法可以满足您的需要:它左连接所有继承的表并返回适当的类型实例每行。You can't query abstract base classes. For multi-table inheritance you can use django-model-utils and it's
InheritanceManager
, which extends standardQuerySet
withselect_subclasses()
method, which does right that you need: it left-joins all inherited tables and returns appropriate type instance for each row.这是模型中多态性的一个示例(多态性 - 一种形式的多种形式)。
选项 1 - 如果只有一个地方需要处理这个问题:
为了在一两个地方使用一点 if-else 代码,只需手动处理它 - 就开发而言,它可能会更快、更清晰/维护(即也许值得,除非这些查询严重损害您的数据库 - 这是您的判断,取决于具体情况)。
选项 2 - 如果您经常这样做,或者确实需要优雅的查询语法:
幸运的是,django 中有一个处理多态性的库,django-polymorphic - 这些文档将向您展示如何精确地做到这一点。正如您所描述的,这可能是直接查询的“正确答案”,特别是如果您想在很多地方进行模型继承。
选项 3 - 如果您想要一个中途之家:
这种方法具有上述两种方法的缺点,但我过去已经成功地使用它来自动将多个查询集压缩在一起,同时保留了具有包含两种类型模型的一个查询集对象。
查看django-querysetsequence,它管理多个查询集的合并。
它不像 django-polymorphic 那样得到很好的支持或稳定,但仍然值得一提。
This is an example of polymorphism in your models (polymorph - many forms of one).
Option 1 - If there's only one place you deal with this:
For the sake of a little bit of if-else code in one or two places, just deal with it manually - it'll probably be much quicker and clearer in terms of dev/maintenance (i.e. maybe worth it unless these queries are seriously hammering your database - that's your judgement call and depends on circumstance).
Option 2 - If you do this quite a bit, or really demand elegance in your query syntax:
Luckily there's a library to deal with polymorphism in django, django-polymorphic - those docs will show you how to do this precisely. This is probably the "right answer" for querying straightforwardly as you've described, especially if you want to do model inheritance in lots of places.
Option 3 - If you want a halfway house:
This kind of has the drawbacks of both of the above, but I've used it successfully in the past to automatically do all the zipping together from multiple query sets, whilst keeping the benefits of having one query set object containing both types of models.
Check out django-querysetsequence which manages the merge of multiple query sets together.
It's not as well supported or as stable as
django-polymorphic
, but worth a mention nevertheless.如果您需要在基类上进行查询,请不要使用抽象基类。请改用具体的基类。
Don't use an abstract base class if you need to query on the base. Use a concrete base class instead.
这种情况我想也没有其他办法了。
为了优化,您可以避免从抽象
StellarObject
继承,并将其用作通过 FK 连接到Star
和Planet
对象的单独表。这样他们两个都会有 ie。
star.stellar_info.description
。其他方法是添加额外的模型来处理信息并在多对多关系中使用 StellarObject 作为
through
。In this case I think there's no other way.
For optimization, you could avoid inheritance from abstract
StellarObject
and use it as separate table connected via FK toStar
andPlanet
objects.That way both of them would have ie.
star.stellar_info.description
.Other way would be to add additional model for handling information and using StellarObject as
through
in many2many relation.如果您希望根据各自的子类将不同的子类行为与对象联系起来,我会考虑放弃抽象继承模式或具体的基本模式。
当您通过父类查询时(听起来您想要这样做),Django 将生成的对象视为父类的对象,因此访问子类级别的方法需要将对象重新转换为它们的“正确”对象动态子类,以便他们可以看到这些方法...此时,挂在父类级别方法上的一系列 if 语句可以说是一种更干净的方法。
如果上述子类行为不是问题,您可以考虑将自定义管理器附加到抽象基类,通过原始 SQL 将模型缝合在一起。
如果您主要对将一组离散的相同数据字段分配给一堆对象感兴趣,我会沿着外键进行关联,就像 bx2 所建议的那样。
I would consider moving away from either an abstract inheritance pattern or the concrete base pattern if you're looking to tie distinct sub-class behaviors to the objects based on their respective child class.
When you query via the parent class -- which it sounds like you want to do -- Django treats the resulting ojects as objects of the parent class, so accessing child-class-level methods requires re-casting the objects into their 'proper' child class on the fly so they can see those methods... at which point a series of if statements hanging off a parent-class-level method would arguably be a cleaner approach.
If the sub-class behavior described above isn't an issue, you could consider a custom manager attached to an abstract base class sewing the models together via raw SQL.
If you're interested mainly in assigning a discrete set of identical data fields to a bunch of objects, I'd relate along a foreign-key, like bx2 suggests.
据我所知,这是 Django ORM 的唯一方法。目前实现的抽象类是将类的公共属性抽象为超类的便捷机制。 ORM 不提供类似的查询抽象。
您最好使用另一种机制在数据库中实现层次结构。实现此目的的一种方法是使用单个表并使用类型“标记”行。或者,您可以为另一个保存属性的模型实现通用外键(后者甚至对我来说听起来也不正确)。
As far as I know it is the only way with Django's ORM. As implemented currently abstract classes are a convenient mechanism for abstracting common attributes of classes out to super classes. The ORM does not provide a similar abstraction for querying.
You'd be better off using another mechanism for implementing hierarchy in the database. One way to do this would be to use a single table and "tag" rows using type. Or you can implement a generic foreign key to another model that holds properties (the latter doesn't sound right even to me).
您可以在 StellarObject 类中创建一个静态方法 all(),在其中手动聚合所有恒星物体(包括行星和恒星)的列表,然后从任何地方调用它 StellarObject.all()。不要忘记文档类型以轻松访问对象的属性。
也许是这样的:
然后:
You can create a static method all() in your StellarObject class where you manually aggregate a list of all stellar objects, both Planets and Stars and then call it from everywhere StellarObject.all(). Don't forget the doctype to access attributes of objects easily.
Maybe something like that:
and then :