【问题标题】:Django Multiple Form Submission: One form overwriting all form objectsDjango 多表单提交:一个表单覆盖所有表单对象
【发布时间】:2023-09-17 04:17:01
【问题描述】:

我想编写一个显示多个表单的视图,每个表单对应一个对象查询返回的模型实例。这样,用户可以一次填写多个表格,然后所有对象的数据都保存在POST 上。这是我所拥有的:

查看:

def formView(request, attr):
     object_list = MyModel.objects.filter(attribute1=attr)
     if request.method == 'POST':
          form_list = [MyModelForm(request.POST, instance=x) for x in object_list]
          if all(form.is_valid for form in form_list):
              for form in form_list:
                  form.save()
              return HttpResponseRedirect('/thanks/')
     else:
          form_list = [MyModelForm(instance=x) for x in object_list]
     return render(request, 'formView.html', {'form_list':form_list})

HTML:

    <table>
        <thead>
            <tr>
                <th>Field1</th>
                <th>Field2</th>
                <th>Field3</th>
            </tr>
        </thead>
        <tbody>
            {% for form in form_list %}
            <tr>
                <form method="post">{% csrf_token %}
                {% for field in form %}
                <td>{{ field }}</td>
                {% endfor %}
                <td><input type="submit" value="Submit" /></td>
                </form>
            </tr>
            {% endfor %}
        </tbody>
    </table>

如果我错了,请纠正我,但这是我认为我的代码当前正在执行的操作:当用户填写页面上的表单然后单击 Submit 按钮之一时,该表单对应于该 @987654325 @按钮只将其数据发送到我的视图,然后继续将该数据保存到form_list中的所有表单中,从而将相同的数据写入我数据库中的所有相应对象(这显然不是我想要的) .

我想要的是整个页面的单个Submit 按钮,以便页面中每个表单的数据与其关联的表单一起发送回并保存到数据库中相应的相应对象。这可能吗?

【问题讨论】:

  • 浏览器只会发送与您的提交按钮相关的表单,而不会提交其他表单。这只是 W3C 浏览器行为。因此,每个&lt;form&gt; 都有自己的方法(GET/POST)和操作 URI。您想要的是使用现在已经回答的 FormSet;请注意,它使用单个表单元素来呈现。

标签: django django-forms


【解决方案1】:

您是否尝试过与模型关联的 FormSet?您可以定义查询以选择要编辑的对象,Django 会处理(几乎)所有事情。 https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#changing-the-queryset

如果您想采用您的方法(每页不止一个 Form,或每页不止一个 FormSet),请使用 prefix 属性: https://docs.djangoproject.com/en/dev/ref/forms/api/#subclassing-forms 并且只使用一个&lt;form&gt; 标签和一个Submit 按钮。

例子: http://docs.djangoproject.com/en/dev/topics/forms/formsets/#using-more-than-one-formset-in-a-view

【讨论】:

  • 我尝试了 FormSet 方法,因为我喜欢使用 Django 的所有“包含电池”部分,但后来意识到它不起作用,因为我需要排除一个表单字段但仍然显示它的价值,我不能在{% for form in formset %} 循环中这样做。但是,prefix 方式效果很好,+1 提到我只需要一个 &lt;form&gt; 标记和一个 Submit 按钮。
  • 我最近才开始使用表单,所以当您说不能排除某个表单字段时,我不确定我是否理解您的意思。您是否尝试使用此处解释的第三种方法? docs.djangoproject.com/en/dev/topics/forms/modelforms/…什么问题?
  • 排除表单字段不是问题,当我使用{% for form in formset %} 遍历表单时,问题出现在我的模板中。如果我在定义我的表单集时排除了一个字段,它不会出现在我的模板中该循环内的任何表单中。但是我仍然想显示排除字段的数据,并且我无法访问我的表单集中的数据。因此,使用prefix 方法,而不是仅将简单的form_list 从我的代码示例发送到我的模板,我创建了一个value,form 元组列表,然后我可以在模板中同时循环这两者。
  • 是的,不能排除字段,否则就没有了。但是我不明白为什么你不能做第三种方法:{{ formset.management_form }}{% for form in formset %}{% for field in form %}{% endfor %}{% endfor %}
  • 这可能是我发布的代码比我想做的更简单的错。我想遍历排除特定字段的表单,因为我不希望为现有对象编辑该字段中的值。但是,我仍然希望在每个表单旁边显示这些值,以帮助用户识别对象实例。据我所知,没有办法在{% for form in formset %} 循环中循环排除字段的值。所以我创建了一个数据结构,其中包括我想要循环的表单和我想要显示的数据(但不要被编辑)。
【解决方案2】:

尝试在表单中使用prefix,以便单独标识每个表单。 x.id 作为前缀会更好。

类似:

def formView(request, attr):
     object_list = MyModel.objects.filter(attribute1=attr)
     if request.method == 'POST':
          form_list = [MyModelForm(request.POST, instance=x, prefix=str(x.id)) for x in object_list]
          if all(form.is_valid for form in form_list):
              for form in form_list:
                  form.save()
              return HttpResponseRedirect('/thanks/')
     else:
          form_list = [MyModelForm(instance=x, prefix=str(x.id)) for x in object_list]
     return render(request, 'formView.html', {'form_list':form_list})

【讨论】:

  • 感谢您的回复!这似乎是实现我正在寻找的最快的方法,因为它只需要对我的代码进行最小的更改。但是,现在当用户在其中一个表单上单击Submit 时,其余表单将变为空白,这会触发这些表单的所有field.errors。为什么会这样?
  • @GChorn,查看request.POST中所有表单的数据。
  • 我从阅读另一个答案中意识到了问题——使用这种方法我需要使用一个只有一个Submit 按钮的&lt;form&gt; 标签,然后在我的form_list 内迭代&lt;form&gt; 元素。
  • @GChorn,是的,所有表单都应该在一个 &lt;form&gt; 标签和一个 submit 按钮中。