You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用post-save信号调用函数时触发Recursion error问题排查

解决自关联Category模型的递归深度错误问题

我来帮你排查这个maximum recursion depth error的问题,核心有两个关键错误导致了这个情况,咱们一步步拆解:

1. post_save信号触发了无限递归循环

你在save_path信号处理函数里调用了instance.save(),这会再次触发post_save信号,而新的信号处理又会再次调用save()——这个循环会一直重复,直到达到Python的递归深度上限,直接抛出错误。

2. 路径构建函数的逻辑错误

你的_recurse_for_parent函数没有正确把父节点的ID添加到路径里,而且还犯了一个变量名错误:模型里的字段是path,但你给instance.children赋值了,这完全不匹配。

修复方案:用模型save方法替代信号(更可靠)

信号虽然方便,但在这种需要修改模型字段并保存的场景下,很容易触发递归。更稳妥的方式是重写模型的save方法来维护path字段:

from django.db import models
from your_app.models import SEO, MetaData  # 替换成你的实际导入路径

class Category(SEO, MetaData):
    parent = models.ForeignKey('self', blank=True, null=True, verbose_name='parent category', on_delete=models.CASCADE)
    path = models.ArrayField(models.IntegerField(), blank=True, default=list)  # 设default为空列表,避免null值

    def save(self, *args, **kwargs):
        # 构建当前分类的路径
        if self.parent:
            # 复制父节点的路径,避免修改父节点的原数据
            parent_path = self.parent.path.copy() if self.parent.path else []
            # 把父节点的ID追加到路径末尾
            parent_path.append(self.parent.id)
            self.path = parent_path
        else:
            # 没有父节点(根分类),路径设为空列表
            self.path = []
        # 调用父类的save方法完成保存
        super().save(*args, **kwargs)

如果坚持用信号的修复方法

如果你一定要用post_save信号,需要避免无限递归,并且修正路径逻辑:

def _recurse_for_parent(cat_obj, path=None):
    if not path:
        path = []
    if cat_obj.parent is not None:
        # 先递归获取父节点的路径
        path = _recurse_for_parent(cat_obj.parent, path)
        # 将父节点ID加入路径
        path.append(cat_obj.parent.id)
    return path

def save_path(sender, instance, **kwargs):
    # 先计算预期的路径
    expected_path = []
    if instance.parent_id:
        expected_path = _recurse_for_parent(instance.parent)
        expected_path.append(instance.parent.id)
    
    # 只有当当前path和预期不符时才更新,避免无限递归
    if instance.path != expected_path:
        instance.path = expected_path
        # 使用update_fields只更新path字段,减少不必要的操作
        instance.save(update_fields=['path'])

post_save.connect(save_path, sender=Category)

这个方法里,我们通过update_fields限定只更新path字段,并且增加了路径对比,只有当路径需要修改时才执行保存,最大程度避免递归触发。


内容的提问来源于stack exchange,提问作者user3541631

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:14:00