Django:使用表单新增的非模型字段创建实例报错‘Book object has no attribute start_tag_number’排查
问题排查与修复方案
嘿,这个报错的原因其实很直观——你搞错了表单字段和模型实例的属性范围!
为什么会报错?
self.instance是Book模型的实例,而start_tag_number是你在BookChangeStatusForm这个表单类里新增的自定义字段,它根本不属于Book模型,所以当你写self.instance.start_tag_number时,程序自然会告诉你Book对象没有这个属性。
修复方法
你需要从表单的清理后数据里获取start_tag_number的值,而不是从模型实例里拿。修改你的save方法如下:
def save(self, commit=True): # 从表单的cleaned_data字典中获取自定义字段的值 start_tag = self.cleaned_data.get('start_tag_number') # 创建新的Book实例,复用旧实例的name和library,用表单的数值作为tag_number instance = Book.objects.create( name=self.instance.name, library=self.instance.library, tag_number=start_tag, ) return instance
额外的优化建议
既然你的表单Meta里设置了fields = (),说明你不需要渲染任何Book模型的字段,其实用普通的Form类代替ModelForm会更贴合这个场景(毕竟你只是用表单收集一个数值,然后创建新实例):
优化后的表单类
class BookChangeStatusForm(forms.Form): start_tag_number = forms.IntegerField(label='Start number', min_value=1) # 初始化时接收旧的Book实例,方便后续复用数据 def __init__(self, *args, **kwargs): self.old_book = kwargs.pop('instance') super().__init__(*args, **kwargs) def save(self, commit=True): start_tag = self.cleaned_data.get('start_tag_number') return Book.objects.create( name=self.old_book.name, library=self.old_book.library, tag_number=start_tag, )
对应的视图调整
因为用了普通Form,需要在视图里把旧实例传递给表单,重写get_form_kwargs即可:
class BookTagView(core.UpdateView): form_class = BookChangeStatusForm template_name = 'book/book_tag.html' success_url = reverse_lazy('book:book-list') model = Book # 加上model,让UpdateView能自动获取对应的实例 def get_form_kwargs(self): kwargs = super().get_form_kwargs() # 将当前的Book实例传递给表单 kwargs['instance'] = self.get_object() return kwargs
这样调整后,代码逻辑更清晰,也避免了模型实例和表单字段的混淆问题。
内容的提问来源于stack exchange,提问作者Ken Ist
相关产品推荐
相关产品推荐

