具有显式同意的树查询,无需可配置性
项目描述
使用邻接表和递归公用表表达式查询Django模型树。支持PostgreSQL、sqlite3(3.8.3或更高版本)、MariaDB(10.2.2或更高版本)和MySQL(8.0或更高版本,如果未运行 ONLY_FULL_GROUP_BY)。
支持Django 3.2或更高版本,Python 3.8或更高版本。有关更多详细信息,请参阅GitHub动作构建。
功能和限制
仅支持整数和UUID主键(目前如此)。
允许指定兄弟之间的排序。
使用正确的深度定义,其中根节点的深度为零。
父外键目前必须命名为 "parent"(但为什么你想将其命名为不同呢?)
公共表表达式添加的字段始终是 tree_depth、tree_path 和 tree_ordering。这些字段名称不能更改。tree_depth 是一个整数,tree_path 是主键数组,tree_ordering 是用于在同级节点中排序的值数组。请注意,tree_path 和 tree_ordering 的内容可能会更改。您不应依赖它们的内容。
除了上述字段外,该包还仅添加了用于排序同级节点和过滤祖先及后代查询集的方法。其他功能可能很有用,但不会因为可以这样做而添加到包中。
与 Django 的其他树管理解决方案相比,代码量少,相对简单。没有冗余值,因此导致数据损坏的唯一方法是引入树结构中的循环(使其成为图)。TreeNode 抽象模型类对此提供了一些保护。
由于 MySQL/MariaDB 不支持数组并要求我们提前提供 tree_path 和 tree_ordering 的最大长度,因此该包仅支持最多 50 级别的树。
以下是一篇博客文章,其中提供了一些关于 django-tree-queries 存在原因 的额外见解(希望如此)。
使用方法
使用 pip 安装 django-tree-queries。
扩展 tree_queries.models.TreeNode 或使用 tree_queries.query.TreeQuerySet 构建自己的查询集和/或管理器。为了方便起见,TreeNode 抽象模型已包含一个 parent 外键,并使用模型验证来防止循环。
如果您需要相应的附加字段或公共表表达式,请调用 with_tree_fields() 查询集方法。
如果您想根据特定模型字段对树同级节点进行排序,请调用 order_siblings_by("field_name") 查询集方法。请注意,Django 的标准 order_by() 方法不受支持——节点根据 深度优先搜索算法 返回。
如果您想默认将树字段添加到查询中,请使用 TreeQuerySet.as_manager(with_tree_fields=True) 创建管理器。
在文档更完整之前,我必须将您指引到 测试套件 以获取额外的说明和用法示例,或查看下面的食谱。
食谱
基本模型
以下两个示例都扩展了 TreeNode,它提供了一些便利的实用工具和一个防止树结构中循环的模型验证方法。公共表表达式可能可以防止此类循环,但这将涉及性能损失,我们不想这么做——毕竟,这是库的文档限制(非目标)。
基本树节点
from tree_queries.models import TreeNode
class Node(TreeNode):
name = models.CharField(max_length=100)
具有同级排序的树节点
具有相同父级的节点可以在彼此之间排序。默认情况下,按主键排序同级节点,但这并不总是很有用。
from tree_queries.models import TreeNode
class Node(TreeNode):
name = models.CharField(max_length=100)
position = models.PositiveIntegerField(default=0)
class Meta:
ordering = ["position"]
向查询集添加自定义方法
from tree_queries.models import TreeNode
from tree_queries.query import TreeQuerySet
class NodeQuerySet(TreeQuerySet):
def active(self):
return self.filter(is_active=True)
class Node(TreeNode):
is_active = models.BooleanField(default=True)
objects = NodeQuerySet.as_manager()
查询树
所有示例都假定上面提到的 Node 类。
基本用法
# Basic usage, disregards the tree structure completely.
nodes = Node.objects.all()
# Fetch nodes in depth-first search order. All nodes will have the
# tree_path, tree_ordering and tree_depth attributes.
nodes = Node.objects.with_tree_fields()
# Fetch any node.
node = Node.objects.order_by("?").first()
# Fetch direct children and include tree fields. (The parent ForeignKey
# specifies related_name="children")
children = node.children.with_tree_fields()
# Fetch all ancestors starting from the root.
ancestors = node.ancestors()
# Fetch all ancestors including self, starting from the root.
ancestors_including_self = node.ancestors(include_self=True)
# Fetch all ancestors starting with the node itself.
ancestry = node.ancestors(include_self=True).reverse()
# Fetch all descendants in depth-first search order, including self.
descendants = node.descendants(include_self=True)
# Temporarily override the ordering by siblings.
nodes = Node.objects.order_siblings_by("id")
广度优先搜索
没有人想广度优先搜索,但如果您仍然需要它,可以按以下方式实现
nodes = Node.objects.with_tree_fields().extra(
order_by=["__tree.tree_depth", "__tree.tree_ordering"]
)
按深度过滤
如果您只想获取前两级别的节点
nodes = Node.objects.with_tree_fields().extra(
where=["__tree.tree_depth <= %s"],
params=[1],
)
表单字段
django-tree-queries 提供了一个模型字段和一些表单字段,这些字段增强了默认的外键字段和选择字段,并使用破折号等来可视化树结构。这些字段包括 tree_queries.fields.TreeNodeForeignKey,tree_queries.forms.TreeNodeChoiceField,tree_queries.forms.TreeNodeMultipleChoiceField。
项目详情
下载文件
下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。