在 Django 中重新显示包含先前提交的错误数据的表单

Redisplay Form with Previously Submitted Data on Error in Django

提交数据到re-display formset 遇到错误后如何获取? Django 书谈到了这一点,我能够使它在表单中工作,但不能在表单集中工作。

场景: 有 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>

在Shell中:

>>> 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']

表单集与表单完全相同,只需使用 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>

查找如何手动呈现表单, 或使用像 crispy forms 或 django-bootstrap3 之类的东西,它有模板标签来处理所有这些。

事实证明,我需要进行以下更改。这里的关键是将"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>

想要的结果:

现在用户无需重新填写整个表格就知道自己做错了什么或遗漏了什么。