Django使用modelformset_factory时form_valid报错,求问题原因
问题原因
你继承的CreateView是为单个模型表单设计的,它的form_valid方法默认只接受单个Form实例,而非FormSet。当你传入表单集并调用super().form_valid(formset)时,父类方法会按照单个表单的逻辑处理:它会调用传入参数(表单集)的save()方法,而表单集的save()返回的是模型对象列表,后续父类逻辑会把这个列表当成单个模型对象去访问__dict__属性,从而触发'list' object has no attribute '__dict__'错误。
解决方案
不需要调用父类的form_valid方法,直接在自定义的form_valid中完成数据保存后,手动实现重定向和成功消息提示即可。更合理的做法是将视图基类从CreateView换成FormView(FormView更适合处理表单集这类非单个模型创建的场景)。
修改后的完整代码
from django.shortcuts import redirect from django.contrib import messages from django.views.generic.edit import FormView from django.utils.decorators import method_decorator from django.contrib.auth.decorators import login_required from django.contrib.messages.views import SuccessMessageMixin from django.urls import reverse_lazy TraitementFormSet = modelformset_factory( Traitement, form=TraitementForm, fields=('arv_num','arv_reg','arv_reg_pre','arv_deb_dat','arv_fin_dat','arv_enc','arv_rai_eff','arv_rai_vir','arv_rai_gro','arv_rai_per','arv_rai_inc','arv_rai_aut','arv_rai_aut_pre'), extra=1 ) @method_decorator(login_required, name="dispatch") class TraitementFormSetCreate(SuccessMessageMixin, FormView): template_name = "ecrf/traitements_form.html" success_message = "Fiche Traitement créée." success_url = reverse_lazy('ecrf:inclusion_list') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['formset'] = TraitementFormSet(queryset=Traitement.objects.filter(pat=self.kwargs['pk'])) context['action'] = 'create' context['patient'] = Patient.objects.get(ide=self.kwargs['pk']) context['model_name'] = Traitement._meta.model_name # 适配FormView无self.object的特性 context['is_locked'] = None return context def post(self, request, *args, **kwargs): formset = TraitementFormSet(request.POST) if formset.is_valid(): return self.form_valid(formset) else: return self.form_invalid(formset) def form_valid(self, formset): patient = Patient.objects.get(ide=self.kwargs["pk"]) traitements = formset.save(commit=False) for traitement in traitements: traitement.pat = patient traitement.arv_sai_log = self.request.user.username traitement.save() # 手动触发成功消息 messages.success(self.request, self.success_message) # 直接重定向到成功URL return redirect(self.success_url) def form_invalid(self, formset): print('invalid',formset.errors) context = self.get_context_data(formset=formset) return self.render_to_response(context)
关键修改点
- 将视图基类从
CreateView改为FormView,避免父类单个表单逻辑的干扰 - 移除
model和form_class属性(FormView不需要这些) - 在
form_valid中去掉对super().form_valid()的调用,手动实现重定向和成功消息 - 调整
get_context_data中is_locked的逻辑,适配FormView无self.object的特性
内容的提问来源于stack exchange,提问作者SLATER
相关产品推荐
相关产品推荐

