【问题标题】:Redisplay Form with Previously Submitted Data on Error in Django在 Django 中使用先前提交的错误数据重新显示表单
【发布时间】:2015-03-13 03:20:03
【问题描述】:

遇到错误后如何让提交的数据在表单集中重新显示? Django book 谈到了这一点,我能够使它在一个表单中工作,而不是在一个表单集中。

场景: 有 2 个必填字段 - 名字和姓氏。如果我只在名字字段中输入“John”而将姓氏字段留空并单击提交,我将收到一条错误消息,指出这两个字段都是必需的,并且表单清除了带有错误消息的提交数据。我想在名字字段中看到名字“John”以及错误消息。

非常感谢!

**forms.py**

from django import forms
from django.forms.formsets import BaseFormSet


class NameForm (forms.Form):

    first_name = forms.CharField (max_length = 20, required = False)
    last_name = forms.CharField (max_length = 20, required = False)


class BaseNameFormSet (BaseFormSet):

    def clean (self):
        if any (self.errors):
            return

        firstnames = []
        lastnames = []
        for form in self.forms:
            firstname = form.cleaned_data.get ('first_name')
            lastname = form.cleaned_data.get ('last_name')

            if (firstname in firstnames) or (lastname in lastnames):
                raise forms.ValidationError ('First or last name must be unique')
            if (firstname == '') or (lastname == ''):
                raise forms.ValidationError ('Both first and last name must be filled out')

            firstnames.append (firstname)
            lastnames.append (lastname)

        return self.cleaned_data


**views.py**

from django.shortcuts import render
from django.forms.formsets import formset_factory
from nameform.forms import NameForm
from nameform.forms import BaseNameFormSet
from nameform.addName import webform


# Create your views here.
def addname (request):

    # Set maximum to avoid default of 1000 forms.
    NameFormSet = formset_factory (NameForm, formset = BaseNameFormSet, extra = 2, max_num = 5)

    if request.method == 'POST':
        # Django will become valid even if an empty form is submitted. Adding initial data causes unbound form and
        # trigger formset.errors
        # formset = NameFormSet (request.POST, initial = [{'first_name': 'John', 'last_name': 'Doe'}])
        formset = NameFormSet (request.POST)

        if formset.is_valid ():
            location = request.POST ['site']
            data = formset.cleaned_data

            for form in data:
                # Retrieving the value in an empty dictionary will result key error as it doesn't exist
                # firstname = form ['first_name']
                # lastname = form ['last_name']

                firstname = form.get ('first_name')
                lastname = form.get ('last_name')

                if firstname and lastname:
                    webform (firstname, lastname, location)

            context = {'data': data, 'location': location}

            return render (request, 'nameform/success.html', context)

    else:
        formset = NameFormSet ()

    return render (request, 'nameform/addname.html', {
        'formset': formset,
        'first_name': request.POST.get ('first_name', ''),
        'last_name': request.POST.get ('last_name', '')
    })


**addname.html**

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>Add Name</title>
</head>
<body>
    <h1>Add Name</h1>
    <form action="" method="POST">{% csrf_token %}
        <h3>Location:</h3>
        <p><input type="radio" id="radio1" name="site" value="Location_A" checked>
        <label for="radio1">Location A</label>
        <input type="radio" id="radio2" name="site" value="Location_B">
        <label for="radio2">Location B</label></p>
        {{ formset.management_form }}
        <label for="id_form-0-first_name">First Name:</label>
        <input id="id_form-0-first_name" maxlength="20" name="form-0-first_name" type="text" value="{{ first_name }}" />
        <label for="id_form-0-last_name">Last Name:</label>
        <input id="id_form-0-last_name" maxlength="20" name="form-0-last_name" type="text" value="{{ last_name }}" />
        <p><label for="id_form-1-first_name">First Name:</label>
        <input id="id_form-1-first_name" maxlength="20" name="form-1-first_name" type="text" value="{{ first_name }}" />
        <label for="id_form-1-last_name">Last Name:</label>
        <input id="id_form-1-last_name" maxlength="20" name="form-1-last_name" type="text" value="{{ last_name }}" /></p>
        <p><input type="submit" value="Add Name"></p>
    </form>

    {% if formset.errors %}
    <p style="color: red;">Please correct the error below:</p>
    {{ formset.non_form_errors }}
    {% endif %}

</body>
</html>

在外壳中:

>>> partial_data = {
...     'form-TOTAL_FORMS': '2',
...     'form-INITIAL_FORMS': '0',
...     'form-MAX_NUM_FORMS': '3',
...     'form-0-first_name': 'John',
...     'form-0-last_name': 'Doe',
...     'form-1-first_name': 'James',
...     'form-1-last_name': ''
... }
>>> 
>>> NameFormSet = formset_factory (NameForm, formset = BaseNameFormSet)
>>> formset = NameFormSet (partial_data)
>>> 
>>> formset.is_valid ()
False
>>> formset.errors
[{}, {}]
>>> formset.non_form_errors ()
['Both first and last name must be filled out']

【问题讨论】:

    标签: html django forms


    【解决方案1】:

    事实证明,我需要进行以下更改。这里的关键是将“first_name_0”变量与html中的输入字段“form-0-first_name”相关联。如果你把变量和html输入框的名字都设置成一样,会报错。

    例如:

    return render (request, 'nameform/addname.html', {
            'formset': formset,
            'form-0-first_name': request.POST.get ('form-0-first_name', '')})
    

    Django 不喜欢这种格式。而是使其与我在下面的内容相同,以将哪个输入字段与变量匹配。

    views.py

    ...
    ...
    ...
        return render (request, 'nameform/addname.html', {
            'formset': formset,
            'first_name_0': request.POST.get ('form-0-first_name', ''),
            'last_name_0': request.POST.get ('form-0-last_name', ''),
            'first_name_1': request.POST.get ('form-1-first_name', ''),
            'last_name_1': request.POST.get ('form-1-last_name', '')
    

    addname.hmtl

       ...
        ...
        ...
        {{ formset.management_form }}
                <label for="id_form-0-first_name">First Name:</label>
                <input id="id_form-0-first_name" maxlength="20" name="form-0-first_name" type="text" value="{{ first_name_0 }}" />
                <label for="id_form-0-last_name">Last Name:</label>
                <input id="id_form-0-last_name" maxlength="20" name="form-0-last_name" type="text" value="{{ last_name_0 }}" />
                <p><label for="id_form-1-first_name">First Name:</label>
                <input id="id_form-1-first_name" maxlength="20" name="form-1-first_name" type="text" value="{{ first_name_1 }}" />
                <label for="id_form-1-last_name">Last Name:</label>
                <input id="id_form-1-last_name" maxlength="20" name="form-1-last_name" type="text" value="{{ last_name_1 }}" /></p>
    

    想要的结果:

    现在用户无需重新填写整个表单即可知道他们做错了什么或遗漏了什么。

    【讨论】:

      【解决方案2】:

      formsets 和表单完全一样,只是用 request.POST 加载它。

      这是我的一个项目中的一些代码。

      formset = modelformset_factory(PaymentTemplates, extra=extras, form=PaymentTemplateForm)
          if request.POST:
              formset = formset(request.POST)
      

      在您的模板中:

      {% if formset.total_error_count %}
          <ul class="errorList">
          {% for dict in formset.errors %}
              {% for error in dict.values %}
              <li>{{ error }}</li>
              {% endfor %}
          {% endfor %}
          </ul>
      {% endif %}
      

      我假设你也知道你需要这个:

      <form action="..." method="POST">
      {% csrf_token %}
      {{ formset.management_form }}
      {% for form in formset %}
          {{ form.as_table }}
      {% endfor %}
      </form>
      

      查看如何手动呈现表单, 或使用诸如脆的形式,或具有模板标签的 django-bootstrap3 之类的东西来处理所有这些。

      【讨论】:

      • 如何让表单集显示之前提交的数据?我的表单集工作正常,但不确定如何重新显示用户输入的错误。
      • 表单应该填充用户输入的任何内容(这就是 formset(request.POST) 所做的),错误会显示在您设置错误代码的任何位置。
      • 感谢您的投入,但这不是我想要完成的。您上面的方法只是显示错误的另一种方式,类似于我在图像中显示的错误。我想看到的是显示“约翰”的名字输入框。这样用户就不必重新输入名称并知道在显示错误后要修复什么。
      • @dreamzboy 我扩展了我的示例。所有用户输入都保存在formset表单中,因此可以访问。
      猜你喜欢
      • 1970-01-01
      • 2015-08-23
      • 2014-03-13
      • 2014-04-23
      • 2011-12-12
      • 2013-05-13
      • 1970-01-01
      • 1970-01-01
      • 2015-10-17
      相关资源
      最近更新 更多