Django:我如何为异构数据类型的树建模?

时间:2022-04-26 12:34:21

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.

我需要在我的数据库中存储树数据结构,我计划使用django-treebeard或者django-mptt。我的混淆源是每个节点可能是三种不同的可能类型之一:根节点将始终是类型A实体,叶节点是类型C实体,其间的任何东西将是类型B实体。我想知道模拟这种情况的最佳方法。

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.

更新:我首先尝试了模型继承,我认为这可能是最好的方法。不幸的是,django-treebeard的公共API实际上并不是为处理这个而设计的。我最终得到它与GenericForeignKey一起工作。非常感谢您的回答。

4 个解决方案

#1


3  

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)

#2


3  

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

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

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.

树可以是同源的 - 类MyNode是treebeard.Node的直接子类。您的节点可以有一个标志(Root,Middle,Leaf)和A或B或C的FK。这使您在查询MyNode实例时具有类似SQL的灵活性。

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.

这允许您的树生长。节点可以作为类型C(叶子)开始,然后变形为类型B(中间)。您更改状态,然后更改FK。

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.

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

#3


1  

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.

嗯,在某种程度上,已经为你做了很多事情,因为树,API已经固有地识别了根,叶和其他。您可以在各个节点上调用is_root()和is_leaf()来区分它们。

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().

叶子和中间可以是相同类型的实体并且保持相同类型的数据,应用程序根据测试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).

Roots有点特别......他们可能希望保存与整棵树相关的信息,您可能想要一种简单的方法来查找特定的根并保存额外的数据。您可以使用与根节点具有一对一关系的模型(可能使用save方法重载并在允许保存之前检查以确认它指向的节点is_root())。

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.

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

#4


0  

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

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

#1


3  

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)

#2


3  

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

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

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.

树可以是同源的 - 类MyNode是treebeard.Node的直接子类。您的节点可以有一个标志(Root,Middle,Leaf)和A或B或C的FK。这使您在查询MyNode实例时具有类似SQL的灵活性。

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.

这允许您的树生长。节点可以作为类型C(叶子)开始,然后变形为类型B(中间)。您更改状态,然后更改FK。

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.

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

#3


1  

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.

嗯,在某种程度上,已经为你做了很多事情,因为树,API已经固有地识别了根,叶和其他。您可以在各个节点上调用is_root()和is_leaf()来区分它们。

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().

叶子和中间可以是相同类型的实体并且保持相同类型的数据,应用程序根据测试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).

Roots有点特别......他们可能希望保存与整棵树相关的信息,您可能想要一种简单的方法来查找特定的根并保存额外的数据。您可以使用与根节点具有一对一关系的模型(可能使用save方法重载并在允许保存之前检查以确认它指向的节点is_root())。

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.

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

#4


0  

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

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