Django ORM 和闭包表

6 投票
1 回答
1603 浏览
提问于 2025-04-17 15:21

我正在尝试用闭包表来建模一个层级树形结构的数据。树中的每个节点都很简单,定义如下:

class Region(models.Model):
    RegionGuid = models.CharField(max_length=40, unique=True, db_column='RegionGUID', blank=True)
    CustomerId = models.IntegerField(null=True, db_column='CustomerID', blank=True)
    RegionName = models.CharField(max_length=256, db_column='RegionName', blank=True)
    Description = models.TextField(db_column="Description", blank=True)
    class Meta:
        db_table = u'Region'

节点之间的路径是通过下面的闭包表来定义的。这个表包含了一个指向祖先节点的外键(FK),一个指向后代节点的外键,以及祖先节点和后代节点之间的路径长度(也就是节点的数量):

class RegionPath(models.Model):
    Ancestor = models.ForeignKey(Region, null=True, db_column='Ancestor', blank=True)
    Descendant = models.ForeignKey(Region, null=True, db_column='Descendant', blank=True)
    PathLength = models.IntegerField(null=True, db_column='PathLength', blank=True)
    class Meta:
        db_table = u'RegionPath'

现在,我想获取所有的 Region 行以及它们各自的父节点(也就是 RegionPath.PathLength = 1 的情况)。我的 SQL 技能有点生疏,但我觉得 SQL 查询应该是这样的:

SELECT r.* from Region as r 
LEFT JOIN 
(SELECT r2.RegionName, p.Ancestor, p.Descendant from Region as r2 INNER JOIN RegionPath as p on r2.id = p.Ancestor WHERE p.PathLength = 1) AS Parent
on r.id = Parent.Descendant

如果能帮我用 Django 的 QuerySet API 来表达这个查询,我将非常感激。

1 个回答

2

通过给外键添加相关名称,可以像这样做:

class RegionPath(models.Model):
    Ancestor = models.ForeignKey(Region, null=True, db_column='Ancestor', blank=True, related_name="ancestor")
    Descendant = models.ForeignKey(Region, null=True, db_column='Descendant', blank=True, related_name="descendants")
    PathLength = models.IntegerField(null=True, db_column='PathLength', blank=True)
    class Meta:
        db_table = u'RegionPath'

这样你就可以针对这两种关系进行查询了:

children = Region.objects.filter(ancestors__PathLength=1)
parents = Region.objects.filter(descendants__PathLength=1)

我在一个非常相似的模型上进行了测试。你可能需要加上.distinct(),也可能想用select_related()来减少查询次数。

撰写回答