Django自引用Folder模型删除含子项文件夹时触发__init__递归错误
递归错误触发原因
- 核心问题出在重写的模型
__init__方法执行时机不对:Django模型实例的__init__方法会在所有实例化场景被调用,包括级联删除时的待删除对象构造、关联对象预取、表单数据绑定等场景,此时实例内部的字段缓存、关联关系状态还未完全初始化完成。 - 你在
__init__中直接调用filepath()方法,方法内通过if not self.parent_folder判断父节点是否存在,这行代码会触发Django ORM的正向关联查询逻辑:首先检查实例的关联对象缓存,缓存未命中时就会去数据库查询父Folder实例,再构造一个新的父Folder模型实例。 - 新构造的父Folder实例同样会执行
__init__方法,再次调用filepath()触发自身的父节点查询,而此时Django的关联对象缓存还未填充,哪怕是同一个父节点也会被反复重新实例化,无法走到根节点parent_folder为None的终止条件,最终触发无限递归。 - 级联删除场景会批量加载所有层级的子文件夹实例,会放大这个问题,直接触发递归深度超限的报错。
修复方案
方案1:最小改动修复(不调整原有字段结构)
- 不要在
__init__中写业务逻辑,改用Django提供的from_db类方法记录初始路径,该方法仅在实例从数据库完整加载完成后执行,此时实例字段状态完全就绪,不会触发内部状态异常。 - 优化
filepath()方法的判断逻辑,优先判断外键ID字段parent_folder_id,避免无意义的关联对象提前加载,只有确实存在父节点ID时才访问父对象拼接路径。
修复后的模型代码:
class Folder(models.Model): folder_name = models.CharField(max_length=200) parent_folder = models.ForeignKey( 'self', on_delete=models.CASCADE, blank=True, null=True, related_name='child_folders' ) @classmethod def from_db(cls, db, field_names, values): instance = super().from_db(db, field_names, values) # 实例从数据库加载完成后再记录初始路径 instance.initial_filepath = instance.filepath() return instance def filepath(self): # 先判断外键ID,不触发关联查询 if not self.parent_folder_id: return f'media/scripts/{self.folder_name}' return f'{self.parent_folder.filepath()}/{self.folder_name}'
方案2:性能更优的彻底修复(推荐)
动态递归计算文件路径的方式本身存在性能问题,层级越深查询次数越多,还容易出现递归异常。建议直接将filepath作为实际数据库字段存储,重写save方法在文件夹保存时自动计算更新路径,完全避免运行时递归查询:
class Folder(models.Model): folder_name = models.CharField(max_length=200) parent_folder = models.ForeignKey( 'self', on_delete=models.CASCADE, blank=True, null=True, related_name='child_folders' ) # 直接存储路径,避免运行时递归计算 filepath = models.CharField(max_length=500, editable=False) def save(self, *args, **kwargs): if not self.parent_folder_id: self.filepath = f'media/scripts/{self.folder_name}' else: self.filepath = f'{self.parent_folder.filepath}/{self.folder_name}' super().save(*args, **kwargs)
这个方案下不需要重写__init__或者from_db,删除、查询时都不会触发额外的递归查询,从根源上避免递归错误,查询路径时也不需要反复查数据库,性能提升明显。
内容的提问来源于stack exchange,提问作者Balizok
相关产品推荐
相关产品推荐

