Django创建自定义用户报错:CustomUser对象无save_m2m属性
报错原因
运行代码触发如下报错:
'CustomUser' object has no attribute 'save_m2m'
核心触发原因有两个:
save_m2m()是DjangoModelForm实例的方法,不是模型实例自带的方法,给CustomUser模型实例调用这个方法本身属于用法错误。- 重写
save()方法时完全绕开了父类表单的原生保存逻辑,直接用CustomUser.objects.create()生成模型实例,这个实例自然不会附带表单提供的save_m2m方法。
现有代码的其他问题
- 重写
__init__手动从入参里取email、密码的逻辑冗余,且裸except会吞掉所有异常,隐藏潜在问题。表单校验通过后,所有合法字段值都会存在self.cleaned_data中,不需要手动从入参抓取。 - 先调用
objects.create()写入明文密码,再调用set_password(),会产生两次数据库写入,且第一次写入的密码是明文,存在严重安全隐患。 - 把
commit参数默认值写死为False,无视调用方传入的参数值,不符合Django表单save()方法的设计规范。 - 多对多字段的保存逻辑调用位置错误。
修复方案
直接复用父类表单的保存逻辑,不要手动调用objects.create()创建用户,冗余的__init__方法可以整个删除,父类UserCreationForm已经自带密码校验、密码加密逻辑,不需要手动处理。
修复后代码如下:
class CustomUserCreationForm(UserCreationForm): class Meta: model = CustomUser fields = "__all__" def save(self, commit=True, **kwargs): # 调用父类save方法,commit=False时暂不写入数据库,此时表单实例会提供save_m2m方法 user = super().save(commit=False, **kwargs) # 执行Stripe客户创建逻辑 stripe_customer = stripe.Customer.create( email=user.email, name="test", ) # 如果用户模型需要绑定Stripe客户ID,在这里赋值即可 # user.stripe_customer_id = stripe_customer.id if commit: user.save() # 多对多字段在主实例写入数据库后再保存 self.save_m2m() return user
内容的提问来源于stack exchange,提问作者Joseph Adam
相关产品推荐
相关产品推荐

