首页 > 解决方案 > Django forms.ValidationError 未显示在模板中并且提交不起作用

问题描述

在我的 Django 应用程序中,我有一个与我们联系的模板,其中包含以下字段:公司名称、联系电话、电子邮件地址。

我定义了一个 def clean() 来验证电子邮件地址和电话号码,如下所示:

class ContactForm(forms.Form):
    def clean(self):
        super(ContactForm, self).clean()
        email_address = self.cleaned_data.get('email_address')
        contact_no = self.cleaned_data.get('contact_no')

        if validate_email(email_address):
            pattern = re.compile('^[2-9]\\d{9}$')
            if bool(pattern.match(contact_no)):
                return self.cleaned_data
            else: # error not displayed
                raise forms.ValidationError(
                    "Please enter a valid phone number")
        else: # error not displayed
            raise forms.ValidationError(
                "Please enter a valid email address")
        return self.cleaned_data

在“contact_us”模板中,我也正确呈现了错误:

<form method="post" action="{% url 'contact' %}">
    {% csrf_token %}
    {{contact_form.as_p}
    <input type="submit" value="Submit">
</form>

{% if contact_form.errors %}
    {% for field in contact_form %}
        {% for error in field.errors %}
        <div class="alert alert-danger">
            <strong>{{ error|escape }}</strong>
        </div>
        {% endfor %}
    {% endfor %}
{% endif %}

在 views.py 中,即使表单有效并且成功发送了电子邮件,表单也不会在模板上再次呈现(作为全新的表单)。

 def contact(request):
     contact_form = ContactForm()

     if request.method == 'POST':
        contact_form = ContactForm(request.POST)
        if contact_form.is_valid():
            company_name = contact_form.cleaned_data.get('company_name')
            contact_no = contact_form.cleaned_data.get('contact_no')
            email_address = contact_form.cleaned_data.get('email_address')

            # send mail code

            contact_form.save()
            contact_form = ContactForm()
            return render(request, './mysite/contact_us.html', {
                'contact_form': contact_form, 'successful_submit': True})
     return render(request, './mysite/contact_us.html', {
         'contact_form': contact_form,
     })

标签: djangodjango-formsdjango-templates

解决方案


您正在从 ValidationError 提高clean(),因此错误存储在其中form.non_field_errors-您没有显示,因此很明显您看不到它们。

一个记录在案的解决方案self.add_error(<fieldname>, <msg>)改用,

但:

但我正在对两个字段执行验证,因此我使用了常见的 clean() 方法而不是两个单独的 clean_field() 方法。

form.clean()用于交叉验证 - 验证两个相互依赖的字段。在您的示例中,两个字段之间绝对没有依赖关系,您不需要电子邮件的值来验证电话号码,也不需要电话号码来验证电子邮件。IOW,你真的应该使用不同的clean_field方法。

请注意,即使您进行了一些交叉验证,您仍然可以(并且应该)使用clean_field可以单独验证的方法。在您的情况下,您应该有一种validate_email_address方法来验证电子邮件本身(或仅使用form.EmailFieldFWIW),并且clean()只需检查电子邮件是否已经过验证(如果没有,您会在 中发现一些错误self._errors['email_address'])。

虽然我们正在处理它,但您的代码还有很多其他问题。这里:

    if validate_email(email_address):
        pattern = re.compile('^[2-9]\\d{9}$')
        if bool(pattern.match(contact_no)):
            return self.cleaned_data
        else: # error not displayed
            raise forms.ValidationError(
                "Please enter a valid phone number")
    else: # error not displayed
        raise forms.ValidationError(
            "Please enter a valid email address")
    return self.cleaned_data

1/以这种方式预编译正则表达式是完全没用的。要么你在模块的顶层预编译它,所以它只编译一次(至少每个进程),或者你根本不编译它并使用re函数,即if re.match(r"your expression here", your_variable)- re 模块不仅会编译表达式,而且缓存它以备将来重用。

2/ if bool(pattern.match(contact_no))-> 调用bool是多余的,无论如何 Python 都会这样做。

3/ 但是 Django 已经有一个正则表达式验证器,所以这就是你应该使用的。

4/ 控制流程无用复杂,最后的return语句实际上永远不会执行。这个:

    if validate_email(email_address):
        pattern = re.compile('^[2-9]\\d{9}$')
        if bool(pattern.match(contact_no)):
            return self.cleaned_data
        else: # error not displayed
            raise forms.ValidationError(
                "Please enter a valid phone number")
    else: # error not displayed
        raise forms.ValidationError(
            "Please enter a valid email address")

使用提前退出会更具可读性:

    if not validate_email(email_address):
        raise forms.ValidationError(
            "Please enter a valid email address"
            )

    pattern = re.compile('^[2-9]\\d{9}$')
    if not pattern.match(contact_no)):
        raise forms.ValidationError(
            "Please enter a valid phone number"
            )

    return self.cleaned_data

5/ 但这仍然是错误的,因为验证将在第一个检测到的错误时停止 - 您希望一次捕获尽可能多的验证错误,这样用户就不必阅读错误消息,更正一个字段,重新-提交,找到另一个可能在第一次检测到的错误,修复它,重新提交等等。好吧,除非你的目标是让用户很难,以至于他放弃尝试使用你的网站,当然。

6/你的视图代码也是双重错误的。首先,您应该在发送邮件之前保存表单(我假设是 ModelForm),因此如果出现任何问题,您不会丢失数据,然后正如 Daniel 已经提到的,您应该始终在成功发布后重定向(阅读此内容为什么)


推荐阅读