抽象基类的 Django 模型字段
我已经在堆栈溢出周围搜索了这个(可能很简单)问题的答案,但我看到的大多数解决方案似乎过于复杂且难以理解。
我有一个模型“Post”,它是一个抽象基类。模型“Announcement”和“Event”继承自Post。
现在我在其他模型中保留相关的事件和公告列表。例如,我在另一个模型中有“removed_events”和“removed_announcements”字段。
但是,在我的项目中,“removed_events”和“removed_announcements”的处理方式完全相同。无需区分“已删除的事件”和“已删除的公告”之间的歧义。换句话说,一个跟踪“removed_posts”的字段就足够了。
我不知道如何(或者可能不能)创建一个字段“removed_posts”,因为 Post 是抽象的。然而,现在我觉得我在代码中重复了自己(并且必须进行大量混乱的检查,以确定我正在查看的帖子是事件还是公告,并将其添加到适当的位置)删除字段)。
这里最好的选择是什么?我可以使 Posts 成为非抽象的,但 Post 对象本身不应该被创建,而且我不认为我可以在非抽象对象上强制执行此操作。
我对数据库的理解很薄弱,但我的印象是,使 Post 非抽象会使数据库由于连接而变得复杂。这有什么大不了的吗?
最后,其他模型中还有其他字段,我想将相当于 event_list 和announcement_list 的内容压缩到 post_list 中,但这些字段确实需要消除歧义。我可以根据帖子类型过滤 post_list,但是调用 filter() 会比直接单独访问事件和公告列表慢,不是吗?这里有什么建议吗?
非常感谢您阅读本文。
I've searched around stack overflow for an answer to this (probably simple) question, but most of the solutions I see seem overly complicated and hard to understand.
I have a model "Post" which is an abstract base class. Models "Announcement" and "Event" inherit from Post.
Right now I'm keeping related lists of Events and Announcements in other models. For instance, I have "removed_events" and "removed_announcements" fields in another model.
However, in my project, "removed_events" and "removed_announcements" are treated exactly the same way. There is no need to disambiguate between a "removed event" and a "removed announcement." In other words, a field keeping track of "removed_posts" would be sufficient.
I don't know how to (or perhaps can't) create a field "removed_posts," since Post is abstract. However, right now I feel like I'm repeating myself in the code (and having to do a lot of clutter-some checks to figure out whether the post I'm looking at is an event or an announcement and add it to the appropriate removed field).
What is the best option here? I could make Posts non-abstract, but Post objects themselves should never be created, and I don't think I can enforce this on a non-abstract object.
My understanding of databases is weak, but I'm under the impression that making Post non-abstract would complicate the database due to joins. Is this a big deal?
Finally, there are other fields in other models where I'd like to condense things that amount to an event_list and an announcement_list into a post_list, but those fields do need to be disambiguated. I could filter the post_list based on post type, but the call to filter() would be slower than being able to directly access the event and announcement lists separately, wouldn't it? Any suggestions here?
Thanks a ton for reading through this.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Django 中有两种模型子类化 - 抽象基类;和多表继承。
抽象基类本身不会被使用,并且没有数据库表或任何形式的标识。它们只是一种缩短代码的方法,通过对代码中的公共字段集进行分组,而不是在数据库中。
例如:
这是一个人为的示例,但正如您所看到的,
Employee
不是Address
,也不是Employer
。它们都只包含与地址相关的字段。本例中只有两个表;Employee
和Employer
- 它们都包含地址的所有字段。雇主地址无法与数据库级别的雇员地址进行比较 - 地址没有自己的密钥。现在,通过多表继承(从Address中删除abstract=True),Address确实拥有一个完全属于自己的表。这将产生 3 个不同的表;
地址
、雇主
和雇员
。雇主和雇员都将有一个返回地址的唯一外键 (OneToOneField)。您现在可以引用地址,而不必担心它是什么类型的地址。
每个地址都有自己的主键,这意味着它可以单独保存在第四个表中:
如果您需要使用
Post
不用担心帖子是什么类型。如果从子类访问任何 Post 字段,则会产生连接开销;但开销将是最小的。这是一个独特的索引连接,速度应该非常快。只需确保,如果您需要访问
Post
,请在查询集上使用select_lated
。这将避免额外的查询来获取父数据,但会导致连接发生。因此,仅当您需要帖子时才使用选择相关。
最后两个注释;如果帖子既可以是公告又可以是事件,那么您需要执行传统的操作,并通过外键链接到帖子。在这种情况下,子类化将不起作用。
最后一件事是,如果父级和子级之间的连接对性能至关重要,则应该使用抽象继承;并使用通用关系来引用对性能不太重要的表中的抽象帖子。
泛型关系本质上存储这样的数据:
在 SQL 中连接会复杂得多(django 可以帮助你),但它的性能也比简单的 OneToOne 连接低。仅当 OneToOne 连接严重损害应用程序的性能(这可能不太可能)时,您才需要采用此方法。
There are two kinds of model subclassing in Django - Abstract Base Classes; and Multi-Table inheritance.
Abstract Base Classes aren't ever used by themselves, and do not have a database table or any form of identification. They are simply a way of shortening code, by grouping sets of common fields in code, not in the database.
For example:
This is a contrived example, but as you can see, an
Employee
isn't anAddress
, and neither is anEmployer
. They just both contain fields relating to an address. There are only two tables in this example;Employee
, andEmployer
- and both of them contain all the fields of Address. An employer address can not be compared to an employee address at the database level - an address doesn't have a key of its own.Now, with multi-table inheritance, (remove the abstract=True from Address), Address does have a table all to itself. This will result in 3 distinct tables;
Address
,Employer
, andEmployee
. Both Employer and Employee will have a unique foreign key (OneToOneField) back to Address.You can now refer to an Address without worrying about what type of address it is.
Each address will have its own primary key, which means it can be saved in a fourth table on its own:
Multi-table Inheritance is what you're after, if you need to work with objects of type
Post
without worrying about what type of Post it is. There will be an overhead of a join if accessing any of the Post fields from the subclass; but the overhead will be minimal. It is a unique index join, which should be incredibly quick.Just make sure, that if you need access to the
Post
, that you useselect_related
on the queryset.That will avoid additional queries to fetch the parent data, but will result in the join occurring. So only use select related if you need the Post.
Two final notes; if a Post can be both an Announcement AND an Event, then you need to do the traditional thing, and link to Post via a ForeignKey. No subclassing will work in this case.
The last thing is that if the joins are performance critical between the parent and the children, you should use abstract inheritance; and use Generic Relations to refer to the abstract Posts from a table that is much less performance critical.
Generic Relations essentially store data like this:
That will be a lot more complicated to join in SQL (django helps you with that), but it will also be less performant than a simple OneToOne join. You should only need to go down this route if the OneToOne joins are severely harming performance of your application which is probably unlikely.
通用关系和外键是您成功之路上的朋友。定义一个中间模型,其中一侧是通用的,然后另一侧将获得多态模型的相关列表。它只是比标准 m2m 连接模型稍微复杂一点,因为通用侧有两列,一列对应 ContentType(实际上是 FK),另一列对应实际链接模型实例的 PK。您还可以使用标准 FK 参数限制要链接的模型。
你很快就会习惯它。
(现在我有了一个可以用来书写的实际键盘,这里有一个示例:)
您无法像经典的多对多那样过滤到相关集合,但可以使用
Owner
,并巧妙地使用具体类的管理器,您可以到达您想要的任何地方。Generic relationships and foreign keys are your friend in your path to succeed. Define an intermediate model where one side is generic, then the other side will get a related list of polymorphic models. It's just a little more complicated than a standard m2m join model, in that the generic side has two columns, one to ContentType (actually a FK) and the other to the PK of the actual linked model instance. You can also restrict the models to be linked with using standard FK parameters.
You'll get used with it quickly.
(now that I get an actual keyboard to write with, here there is the example:)
You can't filter into the related collection like a classic many-to-many, but with the proper methods in
Owner
, and using the concrete classes' managers smartly, you get everywhere you want.