django - 使用 Django Treebeard 获取祖先时如何防止 N+1 查询?

标签 django django-models django-treebeard

我们正在使用 Django Treebeard 的物化路径对组织层次结构进行建模,如下所示:

Organizational Hierarchy Example

现在组织树中的每个节点都可以有多个任务:

class Organization(MP_Node):
    node_order_by = ['name']
    name = models.CharField(max_length=100)

class Task(models.Model):
    organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
    description= models.TextField()

给定任务列表,我们希望在结果中包含每个任务的完整组织路径。我们如何在不需要 N+1 查询的情况下实现这一点?

例如,组织工厂 1 的预期结果可能是:

<表类="s-表"> <头> 任务名称 组织路径 <正文> 任务一 我的公司/工厂 1/维护 任务二 我的公司/工厂 1/运营 任务三 我的公司/工厂 1 任务四 我的公司/工厂 1/运营

最佳答案

django-treebeard 将物化路径存储在 path 中像这样的字符串形式的列:000100020005002I .在此示例中,以下行是其祖先(假设默认步长为 4):

0001
00010002 
000100020005  
000100020005002I

django-treebeard 所做的是在 Python 中将页面路径拆分为上述位,然后执行数据库查询,如下所示:

Organization.objects.filter(path__in=['0001', '00010002', '000100020005'])`

为了避免 n+1 查询问题,我们需要避免在 Python 中拆分路径,并通过子查询在数据库中执行祖先查找。

模式匹配可用于查看祖先路径是否包含在子路径中:00010002火柴000100020005002I当候选人的路径被用作相关组织路径的模式时:

000100020005002I LIKE 00010002%  --- equals true
SELECT  
  organization.path, 
  ARRAY(
   SELECT 
     name 
   FROM
     organization o_ 
   WHERE 
     organization.path LIKE o_.path || '%' 
  )
FROM 
  organization 
<表类="s-表"> <头> 组织路径 数组 <正文> 0001 {根目录} 00010001 {root, org_a} 00010002 {根, org_b} 000100020001 {root, org_b, org_b1}

Django 不提供开箱即用的解决方案来切换 .filter(path__startswith='pattern') 中的参数。查找(在我们这里的例子中是必需的)。这就是我使用 RawSQL 表达式的原因。

>>> from django.db.models.expressions import RawSQL

>>> orgs = Organization.objects.annotate(
    ancestors=RawSQL(
        """
        ARRAY(
          SELECT name FROM organization o_ 
          WHERE organization.path LIKE o_.path || '%%'
        ) 
        FROM organization
        """,
        params=[],
    )
)

>>> orgs[0].ancestors
['Root', "Org 1", "Org 2", "Org 3"]

关于django - 使用 Django Treebeard 获取祖先时如何防止 N+1 查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71796356/

相关文章:

django - 关于 admin.py list_display 的问题

jquery - 数据表未在 django-datatable-view 中填充数据

Django-nonrel 在管理中使用包含 EmbeddedObjects 的 ListField

django - django-treebeard 物化路径节点的动态排序

sql - 搜索具体化路径树的最右侧节点

django - 是否可以集成django-taggit和django-mptt/django-treebeard?

python - 如何通过 mod-wsgi 使用 FCKEditor 的图像上传和浏览器?

Python:与复杂的数据仓库交互

django - 在保存 Django 模型实例时,相对于用作 ModelField 属性的方法,我的 clean() 和 save() 覆盖应用的顺序是什么?

django - 保存相关模型对象