Django中CBV表单集调用save(commit=False)触发模型自定义save()动作导致子对象重复创建的问题排查与解决
解决Django中CreateView表单集导致子对象重复创建的问题
这个问题我之前也碰到过,根源其实是你的form_valid方法里触发了两次save操作,导致Parent模型的save方法被执行了两次,所以子对象会被重复创建。
让我拆解一下问题:
- 你先用
formset.save(commit=False)拿到未保存的实例,循环给每个实例绑定用户后调用instance.save(),这时候已经触发了一次Parent.save(),创建了子对象。 - 然后你调用
super(Create, self).form_valid(formset),而CreateView的默认form_valid逻辑会再次调用formset.save(),这就又触发了一次Parent.save(),子对象自然就被创建第二次了。
下面给你两个可行的解决方案:
方案一:避免重复保存表单集
直接在处理完实例绑定后返回重定向,跳过父类form_valid里的再次保存操作:
class Create(CreateView): # 你的模型、表单集等配置 def form_valid(self, formset): instances = formset.save(commit=False) for instance in instances: instance.user = self.request.user instance.save() # 这里完成第一次(也是唯一一次)保存,触发子对象创建 # 直接返回成功重定向,不再调用父类的form_valid return redirect(self.get_success_url())
方案二:让模型只在首次创建时生成子对象
如果不想修改视图逻辑,也可以给Parent的save方法加个判断,只有当对象是首次创建(还没有主键)的时候才生成子对象:
class Parent(models.Model): # 你的字段定义 def save(self, *args, **kwargs): # 判断当前对象是否是新创建的(还未存入数据库) is_new_instance = self.pk is None # 先执行父类的save逻辑 super().save(*args, **kwargs) # 只有新对象时才创建子对象 if is_new_instance: self.child_set.create( # 你的子对象字段参数 # 比如 name="默认子对象", ... )
你之前尝试替换child_set.create为手动创建Child实例再保存没用,是因为不管用哪种方式创建子对象,只要Parent.save()被调用两次,就会触发两次子对象创建逻辑。上面的两个方案要么避免重复调用save,要么让save里的子对象创建逻辑只执行一次,都能解决问题。
内容的提问来源于stack exchange,提问作者erickfis
相关产品推荐
相关产品推荐

