重写django-mptt的save方法时,如何基于get_root()生成Category的slug?
解决MPTT分类生成Slug时的未保存实例问题
你的问题核心是调用get_root()时实例还未保存,导致报错。不用先存再更新(避免两次DB操作),可以手动遍历父节点链获取根节点名称,直接在保存前生成Slug:
修改后的代码
from django.db import models from mptt.models import MPTTModel, TreeForeignKey from django.utils.text import slugify class Category(MPTTModel): name = models.CharField(max_length=255, null=False, blank=False) parent = TreeForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='children') slug = models.SlugField(max_length=255, unique=True) # 记得添加Slug字段 def _get_root_name(self): # 手动遍历父节点链,找到根节点的名称 current_parent = self.parent while current_parent and current_parent.parent is not None: current_parent = current_parent.parent return current_parent.name if current_parent else self.name def save(self, *args, **kwargs): if self.parent: root_name = self._get_root_name() self.slug = slugify(f"{root_name} {self.name}") else: self.slug = slugify(self.name) # 调用父类保存方法,仅一次DB操作 super().save(*args, **kwargs)
为什么这个方法高效?
- 避免两次DB写入:直接在保存前生成Slug,只执行一次
save(),不需要先存再更新。 - 内存级遍历:遍历父节点链是内存操作(父节点作为外键关联对象,Django会自动缓存已查询的实例),不会额外触发DB查询,即使分类树有多层,开销也极小。
注意事项
- 确保父节点是已保存的实例:如果是同时创建根节点和子节点(父节点也是新的),需要先保存根节点,再关联它创建子节点,否则父节点未保存时无法遍历链。
- 处理Slug唯一性:如果存在重名的分类组合,建议在Slug后添加序号(比如
root-name-category-name-1),可以在生成Slug后检查是否存在,冲突则自动递增序号。
内容的提问来源于stack exchange,提问作者Srivatsa
相关产品推荐
相关产品推荐

