You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 01:03:09