English 中文(简体)
Django:我该如何对异构数据类型的树进行建模?
原标题:
  • 时间:2008-11-14 20:14:56
  •  标签:

我需要在我的数据库中存储一棵树形数据结构,我打算使用 django-treebeard 或者可能是 django-mptt。 我的困惑之处在于每个节点可能是三种不同的类型之一:根节点始终是类型 A 实体,叶节点是类型 C 实体,中间的任何元素都是类型 B 实体。 我想知道建模这种情况的最佳方法。

更新:我首先尝试了模型继承,我认为这可能是最好的方法。不幸的是,django-treebeard的公共API并没有真正设计用来处理这个问题。最终我使用了GenericForeignKey解决了这个问题。非常感谢提供的答案。

最佳回答

使用从保存树结构的模型到表示节点的内容对象的通用关系如何?

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)
问题回答

你的三种类型可能最容易与基础树一起处理,作为外键关联。

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

这可以让你的树生长。一个节点可以从C型(叶片)变为B型(中间节点) 。你可以改变它的状态,并改变它的外键。

另一种选择有点复杂。

class MyA( treebeard.Node ):
    pass

class MyB( treebeard.Node ):
    pass

class MyC( treebeard.Node ):
    pass

在这种情况下,您无法“变形”节点。当一个节点从一个MyC开始,并且有了子节点,您必须删除原始的MyC实例,并用一个新的MyB版本替换它,该版本具有一个新节点作为子节点。虽然这并非不可能,但可能会很痛苦。

嗯,某种程度上说,对于您来说很多工作都已经完成了,因为树 API 已经天生地识别了根、叶子和其他物品。您可以对每个节点调用 is_root() 和 is_leaf() 方法来区分它们。

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

根有些特别... 它们可能希望保持与整个树相关的信息,你可能需要一种简单的方式来查找特定的根并保存额外的数据。你可以通过具有与根节点一对一关系的模型来实现这一点(可能需要重载保存方法并在允许保存之前检查其指向的节点是否是根节点)。

我的观点是,你可能不需要变得非常复杂才能做到你想做的事情。你所做的区别已经包含在树和它的API的概念中,通过检查节点的上下文,你可能可以使用相同的基本数据实现不同的行为。

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





相关问题
热门标签