0

0

Django 中使用原生 PostgreSQL 递归查询构建菜单树的正确方法

碧海醫心

碧海醫心

发布时间:2026-01-06 14:31:43

|

813人浏览过

|

来源于php中文网

原创

Django 中使用原生 PostgreSQL 递归查询构建菜单树的正确方法

本文详解如何在 django 中安全、准确地执行 postgresql 原生 `with recursive` 查询来获取带层级结构的菜单树,重点解决因字段名不匹配导致的 `valueerror: cannot assign "...": "menuentry.parent" must be a "menuentry" instance` 错误。

在 Django 中使用 Model.objects.raw() 执行原生 PostgreSQL 递归查询时,数据库字段名必须与模型字段定义严格对齐,否则 Django ORM 在实例化模型对象时会因外键字段映射失败而抛出 ValueError。你遇到的错误:

ValueError: Cannot assign "'menu_B_1lvl_entry'": "MenuEntry.parent" must be a "MenuEntry" instance.

根本原因在于:你的 CTE 查询中声明了列别名 my_2ndmenu_items (id, parent, text),其中 parent 对应的是数据库中的 parent_id(即外键关联字段的整数值),但 Django 模型中 parent 是一个 ForeignKey 字段——ORM 期望该列返回的是 parent_id 的整数 ID 值,且列名必须为 parent_id,而非 parent。否则,Django 会尝试将查询结果中名为 parent 的值(可能是字符串或 NULL)直接赋给 MenuEntry.parent 关系属性,从而触发类型校验失败。

✅ 正确做法是:确保 CTE 查询中所有外键字段的列名与 Django 模型中对应的 _id 字段名完全一致

以下是修正后的完整示例:

Opus
Opus

AI生成视频工具

下载
from myapp.models import MenuEntry

query = """
WITH RECURSIVE my_menu_tree (id, parent_id, text, menu_id) AS (
    -- 种子查询:获取指定菜单下的根级 MenuEntry(parent_id IS NULL)
    SELECT id, parent_id, text, menu_id
    FROM menu_menuentry
    WHERE menu_id IN (
        SELECT id FROM menu_menu WHERE name = %s
    )
    UNION ALL
    -- 递归查询:连接子节点
    SELECT m.id, m.parent_id, m.text, m.menu_id
    FROM menu_menuentry m
    INNER JOIN my_menu_tree t ON m.parent_id = t.id
)
SELECT id, parent_id, text, menu_id FROM my_menu_tree;
"""

queryset = MenuEntry.objects.raw(query, ["menu_B"])

# ✅ 现在可以安全访问:
for entry in queryset:
    print(f"{entry.text} (parent_id={entry.parent_id})")
    # 注意:entry.parent 仍为惰性关系,若需访问父对象需确保 parent_id 存在且已查库(raw() 不自动 prefetch)

? 关键修正点总结:

  • 将 CTE 列定义 my_2ndmenu_items (id, parent, text) → 改为 my_2ndmenu_tree (id, parent_id, text, menu_id);
  • 显式列出所有需映射的字段(尤其是 parent_id 和 menu_id),避免隐式列序错位;
  • 使用 UNION ALL 替代 UNION(性能更优,且递归场景通常无需去重);
  • WHERE m.parent_id IS NOT NULL 在递归部分非必需(JOIN 条件已隐含),可移除以提升可读性;
  • menu_id IN (...) 替代 = (...),防止 Menu.name 非唯一时子查询返回多行导致 SQL 错误(Django 要求单值子查询);
  • 若 Menu.name 可能重复,建议为其添加 unique=True 或改用 slug 字段作为查询依据。

⚠️ 注意事项:

  • raw() 查询不会自动处理反向关系或外键对象:entry.parent 仍为未解析的 RelatedManager,访问时会触发额外查询。如需完整对象图,推荐结合 select_related('parent') + 自定义 Manager 或使用 django-mptt/django-treebeard 等专用树形库;
  • 原生 SQL 绕过 Django ORM 校验,务必手动确保字段类型、空值、权限与模型一致;
  • 生产环境建议对 name 参数使用 models.CharField(unique=True) 并加数据库索引,提升子查询性能。

通过精准匹配字段名与 Django 的 _id 命名约定,即可让 raw() 安全承载复杂递归逻辑,兼顾性能与可控性。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

676

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

320

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

346

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

1092

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

356

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

674

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

571

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

412

2024.04.29

PPT动态图表制作教程大全
PPT动态图表制作教程大全

本专题整合了PPT动态图表制作相关教程,阅读专题下面的文章了解更多详细内容。

13

2026.01.07

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 3.4万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号