Django ORM和闭包表

Cad*_*nge 5 python sql django django-queryset transitive-closure-table

我正在尝试使用闭包表对组织为分层树的数据进行建模.表示树中节点的条目没什么特别之处,定义如下.

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'
Run Code Online (Sandbox Code Playgroud)

使用以下闭包表定义节点之间的路径.它包括一个到祖先节点的FK,到后代节点的FK和Ancestor和Descendant之间的路径长度(即节点数):

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'
Run Code Online (Sandbox Code Playgroud)

现在我将如何检索所有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
Run Code Online (Sandbox Code Playgroud)

任何使用Django的QuerySet API表达这一点的帮助都将非常感激.

Árn*_*son 2

通过将 related_name 添加到外键,如下所示:

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'
Run Code Online (Sandbox Code Playgroud)

您可以查询任一关系:

children = Region.objects.filter(ancestors__PathLength=1)
parents = Region.objects.filter(descendants__PathLength=1)
Run Code Online (Sandbox Code Playgroud)

我在一个非常相似的模型上进行了测试。您可能必须添加 .distinct(),您可能需要 select_lated() 来减少查询。