【问题标题】:Django ORM and Closure TablesDjango ORM 和闭包表
【发布时间】:2013-01-25 04:35:42
【问题描述】:

我正在尝试使用闭包表对组织为分层树的数据进行建模。表示树中节点的条目并不花哨,定义如下。

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),但我玩过实现闭包表,也许它会给你一些想法。 gist.github.com/dazld/2174233
  • 感谢您的链接,但我认为这对我没有帮助。我可以计算出查询的逻辑,如果需要,我可以在原始 SQL 中编写查询。我只是被 Django QuerySet API 难住了。
  • 这里有什么特别的原因需要使用闭包表吗?例如,MPTT 有一个非常好的 Django 实现,它解决了同样的问题。
  • 我的 Django 应用程序集成了一个不面向 Django 的遗留数据库,因此仅 Django 的解决方案并不理想。除此之外,闭包表通常是使用平面表实现树状结构的更好方法。见stackoverflow.com/questions/8196175/…
  • @CadentOrange 你找到解决方案了吗?

标签: python sql django django-queryset transitive-closure-table


【解决方案1】:

通过像这样将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() 以减少查询。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-16
    • 2011-10-15
    • 2021-10-01
    • 2016-12-09
    • 2011-02-12
    • 2020-03-21
    • 2011-01-17
    • 2013-05-06
    相关资源
    最近更新 更多