Django中PostgreSQL递归Raw Query报错:MenuEntry.parent赋值异常求助
解决Django中PostgreSQL递归原生查询的ValueError问题
问题根因
你的递归CTE字段定义和数据库表的实际字段顺序不匹配,导致Django解析数据时出错。menu_menuentry表的字段顺序是id, menu_id, parent_id, text,但你定义的CTE是my_2ndmenu_items (id, parent, text),用SELECT *返回数据时,第二个字段是menu_id,会被Django错误地赋值给MenuEntry.parent外键字段——外键需要的是MenuEntry实例,而不是menu_id的数值或文本,所以触发了ValueError。
另外,递归部分的SELECT m.*会返回多余字段,和CTE定义的字段数不匹配,进一步加重了这个问题。
修复后的代码
调整CTE的字段定义,明确指定需要的字段,保证和数据库表、Django模型的字段对应:
queryset = MenuEntry.objects.raw(""" WITH RECURSIVE my_2ndmenu_items (id, menu_id, parent_id, text) AS ( -- 初始查询:获取menu_B的一级菜单条目 SELECT id, menu_id, parent_id, text FROM menu_menuentry WHERE menu_id = (SELECT id FROM menu_menu WHERE name = %s) UNION ALL -- 递归查询:获取子菜单条目 SELECT m.id, m.menu_id, m.parent_id, m.text FROM menu_menuentry m INNER JOIN my_2ndmenu_items r ON m.parent_id = r.id ) SELECT id, menu_id, parent_id, text FROM my_2ndmenu_items """, ["menu_B"])
核心调整说明
- 明确CTE的字段列表,和
menu_menuentry表的实际字段一一对应,避免SELECT *带来的字段顺序混乱 - 用
UNION ALL替代UNION:递归查询不需要去重,UNION ALL性能更优 - 删除递归部分的
WHERE m.parent_id IS NOT NULL:因为INNER JOIN已经确保m.parent_id存在(关联到父条目id),这个条件多余
修复后,Django会正确把parent_id映射到MenuEntry.parent外键,自动转换为MenuEntry实例,不会再触发错误。
内容的提问来源于stack exchange,提问作者Alexander Mirnyy
相关产品推荐
相关产品推荐

