Django ORM和闭包表

6

我将尝试使用闭包表来建模组织成分层树形结构的数据,这些条目将代表树中的节点,它们并不复杂,定义如下。

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、指向后代节点的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来表达这个问题,将不胜感激。


不确定这是否有所帮助,因为它是一种不同的语言(php/codeigniter),但我尝试了实现闭包表,也许它会给你一些想法。https://gist.github.com/dazld/2174233 - danp
谢谢提供链接,但我认为那并不能帮助我。我可以理解查询的逻辑,并且如果必要的话,我可以编写原始SQL查询语句。但是我对Django QuerySet API感到困惑。 - CadentOrange
你在这里使用闭包表的特定原因是什么?例如,有一个非常好的Django实现MPTT,可以解决同样的问题。 - Daniel Roseman
1
我的Django应用程序与一个不是Django导向的遗留数据库集成,因此仅使用Django的解决方案并不理想。除此之外,闭包表通常是使用平面表实现树状结构的更好方法。请参见https://dev59.com/dGsy5IYBdhLWcg3w9i1u。 - CadentOrange
@CadentOrange 你找到解决方案了吗? - user
1个回答

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'

您可以查询任何关系:

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

我在非常相似的模型上进行了测试。您可能需要添加.distinct(),您可能希望使用select_related()来减少查询。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接