Django:如何对异构数据类型的树进行建模?

发布于 2024-07-09 03:18:52 字数 466 浏览 10 评论 0原文

我需要在数据库中存储树数据结构,我计划使用 django-treebeard< /a> 或可能 django-mptt。 我感到困惑的根源在于,每个节点可能是三种不同的可能类型之一:根节点始终是 A 类型实体,叶节点是 C 类型实体,而介于两者之间的任何节点都将是 B 类型实体。 我想知道模拟这种情况的最佳方法。

更新:我首先尝试了模型继承,我认为这可能是最好的方法。 不幸的是 django-treebeard 的公共 API 并不是真正设计来处理这个问题的。 我最终让它与 GenericForeignKey 一起使用。 非常感谢您的回答。

I need to store a tree data structure in my database, for which I plan on using django-treebeard or possibly django-mptt. My source of confusion is that each node could be one of three different possible types: root nodes will always be a type A entity, leaf nodes a type C entity, and anything in between will be a type B entity. I would like to know the best way to model this situation.

update: I first tried model inheritance, and I think that this could be the best way to go. Unfortunately django-treebeard's public API isn't really designed to handle this. I ended up getting it to work with GenericForeignKey. Thank you very much for the answers.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

落墨 2024-07-16 03:18:52

如何使用模型中的通用关系将树结构保存到它所代表的节点的内容对象?

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

class Node(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    object = generic.GenericForeignKey('content_type', 'object_id')

在检索完整树的内容对象时,这可能会导致大量查询,但有 减少所需查询数量的方式方法

# Assuming mptt, as I'm not familiar with treebeard's API

# 1 query to retrieve the tree
tree = list(Node.tree.all())

# 4 queries to retrieve and cache all ContentType, A, B and C instances, respectively
populate_content_object_caches(tree)

How about using a generic relation from the model which will hold the tree structure to the content object for the node it represents?

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

class Node(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    object = generic.GenericForeignKey('content_type', 'object_id')

This could potentially result in a lot of queries when retrieving content objects for the full tree, but there are ways and means of reducing the number of queries required.

# Assuming mptt, as I'm not familiar with treebeard's API

# 1 query to retrieve the tree
tree = list(Node.tree.all())

# 4 queries to retrieve and cache all ContentType, A, B and C instances, respectively
populate_content_object_caches(tree)
琉璃梦幻 2024-07-16 03:18:52

您的三种类型可能最容易处理为与基本树的 FK 关联。

树可以是同质的——MyNode 类是treebeard.Node 的直接子类。 您的节点可以有一个标志(根、中间、叶)和用于 A、B 或 C 的 FK。这允许您在查询 MyNode 实例时具有一些类似于 SQL 的灵活性。

这可以让你的树生长。 节点可以从 C 类型(叶)开始,然后转变为 B 类型(中间)。 你改变了状态,也改变了FK。

另一种选择稍微复杂一些。

class MyA( treebeard.Node ):
    pass

class MyB( treebeard.Node ):
    pass

class MyC( treebeard.Node ):
    pass

在这种情况下,您无法“变形”节点。 当节点作为 MyC 启动并获得子节点时,您必须删除原始 MyC 实例,并将其替换为 MyB 版本有一个新节点作为子节点。 这并非不可能,但可能会很痛苦。

Your three types are probably easiest handled as FK associations with the fundamental tree.

The tree can be homogenous -- class MyNode is a direct subclass of treebeard.Node. Your node can have a flag (Root, Middle, Leaf) and FK's for A or B or C. This allows you some SQL-like flexibility in querying MyNode instance.

This allows your tree to grow. A node can start as a type C (leaf) and then morph to a type B (intermediate). You change the status, and change the FK's.

The alternative is a bit more complex.

class MyA( treebeard.Node ):
    pass

class MyB( treebeard.Node ):
    pass

class MyC( treebeard.Node ):
    pass

In this case, you can't "morph" a node. When a node starts as a MyC, and gets children, you have to remove the original MyC instance, and replace it with a MyB version that has a new node as a child. This isn't impossible, but it can be painful.

晨曦÷微暖 2024-07-16 03:18:52

好吧,在某种程度上,已经为您完成了很多工作,因为树 API 已经固有地识别了根、叶和其他内容。 您可以在各个节点上调用 is_root() 和 is_leaf() 来区分它们。

叶子和中间元素可以是相同类型的实体并保存相同类型的数据,应用程序解释和使用数据的方式取决于测试 is_leaf()。

根有些特殊......它们可能想要保存与整个树相关的信息,并且您可能喜欢一种简单的方法来查找特定的根并保存额外的数据。 您可以使用与根节点具有一对一关系的模型来执行此操作(可能会重载 save 方法,并在允许保存之前检查以确认它指向的节点 is_root())。

总的来说,我的观点是,你可能不需要非常花哨去做你想做的事。 您所做的区别已经封装在树及其 API 的概念中,您可以通过检查节点的上下文来使用相同的基本数据实现不同的行为。

Well, a lot is already done for you, in a way, because roots, leaves and others are already inherently identified by the tree API. You can call is_root() and is_leaf() on individual nodes to distinguish them.

Leaves and in-betweens can be the same type of entity and hold the same type of data, with the way the data is interpreted and used by the application depending on testing is_leaf().

Roots are somewhat special... they might want to hold information pertinent to the whole tree, and you might like a simple way to look up particular roots and hold extra data. You might do this with a model that has a one-to-one relationship with a root node (perhaps with the save method overloaded and checking to confirm that the node it points to is_root() before allowing the save).

My point overall is that you may not need to get very fancy to do what you want. The distinction you're making is already encapsulated in the concept of the tree and its API and you could probably implement different behavior with the same basic data by checking the context of the node.

懒的傷心 2024-07-16 03:18:52

如果树结构是应用程序的组成部分,请考虑使用关系数据库以外的其他数据库。 也许是neo4j?

If a tree structure is an integral part of your application, consider using something else than a relational database. Maybe neo4j?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文