详解Django的 form_invalidated() 函数:表单数据验证失败后的处理逻辑

  • Post category:Python

form_invalid() 是Django视图函数中,处理表单验证失败时的方法。当表单验证失败时,Django自动将验证引发的异常重定向到该函数。具体来说,这个函数会显示表单验证失败信息,和用于填写过程的表单。它可以对错误进行自定义处理,并在需要时,对表单数据提供补救。

通常,form_invalid() 方法用于显示错误信息并重新展示前置表单,以便填写者可以更正错误。另外,我们也可以选择数据恢复、错误信息字段高亮、记录错误信息、发送邮件等操作。

以下是 form_invalid() 的基本使用方法的攻略:

  1. 重写 form_invalid() 方法

我们可以选择在视图类中重写 form_invalid() 方法来实现我们自己的逻辑。Django将在意料之外的错误发生时调用此方法。通过这个方法,我们可以获取到当前视图处理的表单,并进行一些自定义逻辑。

示例:

from django.views.generic.edit import FormView
from .forms import ContactForm

class ContactView(FormView):
    template_name = 'contact.html'
    form_class = ContactForm

    def form_invalid(self, form):
        # Do some business logic here
        return super().form_invalid(form)
  1. 处理表单错误

Django提供了一种简便方式来验证表单数据。使用 is_valid() 方法可以验证表单数据。如果表单无效,可以使用 form.errors 字典对象获取到验证不通过字段的错误信息。

示例:

def form_invalid(self, form):
    error_message = 'Please correct the errors below.'
    return self.render_to_response(
        self.get_context_data(
            error_message=error_message,
            form=form
        )
    )

在这个例子中,当表单无效时,form_invalid() 函数将构建错误信息,并添加到上下文渲染器 get_context_data() 中呈现出来。

  1. 渲染表单观察错误

如果表单数据在表单验证期间无效,Django经常会将表单视为在当前请 求上下文中已经存在的一个对象,并为您呈现此表单。当表单无效时,可以使用form.errors 获取错误信息,并在模板中使用必要的格式呈现。

{% if form.errors %}
    <div class="alert alert-danger">
        <strong>Please correct the following errors:</strong>
        <ul>

        {% for field in form %}
            {% for error in field.errors %}
                <li>{{ error }}</li>
            {% endfor %}
        {% endfor %}

        </ul>
    </div>
{% endif %}

上面 HTML 代码将会查找表单中的错误信息,并呈现它们。如果表单有效,则不会呈现该消息。

  1. 错误隐藏/显示

对于一些表单域,您可能希望将错误消息嵌入到表单控件的HTML中。可以使用以下代码将错误信息嵌入到HTML中。

{% for field in form %}
    {% if field.errors %}
        <div class="form-group has-error">
        {% for error in field.errors %}
            <span class="help-block">{{ error }}</span>
        {% endfor %}
    {% endif %}

    {% if field.help_text %}
        <small class="form-text text-muted">{{ field.help_text }}</small>
    {% endif %}
{% endfor %}

在这个例子中,假设表单控件通常渲染为<input type="text">。如果验证错误发生,则包装div类为has-error的CSS类,并在现有输入中嵌入错误信息,以显示错误发送的字段。发生验证错误时,还可以在输入控件中添加 requiredaria-invalid 属性。

  1. 记录验证错误

当表单验证失败时,我们可以选择记录错误。这可以通过保存错误消息,将其写入日志,或在指定外部服务上发送电子邮件等方式实现。

import logging
log = logging.getLogger(__name__)

def form_invalid(self, form):
    log.error(
        'Unexpected form validation error in contact view; '
        'errors %s',
        form.errors.as_json(),
        exc_info=True,
    )
    return super().form_invalid(form)

在此情况下,我们在视图中记录了错误消息。此日志记录是在请求上下文中发生的,并包含错误消息的JSON表示。当出现错误时,将指定sys.exc_info()事件记录传递给日志记录系统。

这是一个示例视图类:

from logging import getLogger
from typing import Type
from django.views.generic.edit import FormView  
from django.http import HttpResponse
from .forms import ContactForm

log = getLogger(__name__)

class LoggingContactFormView(FormView):

    template_name = 'contact.html'
    form_class = ContactForm

    def form_invalid(self, form):
        log.exception(
            'Unexpected form validation error in contact view; '
            'errors %s',
            form.errors.as_json(),
            exc_info=True
        )
        return super().form_invalid(form)

    def form_valid(self, form):
        log.info(
            'Form submission with valid data %s', form.cleaned_data
        )
        return HttpResponse('Thanks for your submission')
  1. 错误恢复

在某些情况下,验证失败后我们可以选择还原现有表单中可用的数据,以便填写者可以更轻松地纠正其问题。

def form_invalid(self, form):
    context = self.get_context_data(
        form=form,
    )

    try:
        previous_values = self.request.POST.dict()
        previous_values.pop('csrfmiddlewaretoken')
        context.update({
            'request_values': previous_values
        })
    except Exception as e:
        log.exception(e)

    return self.render_to_response(context=context)

在此代码段中,当表单无效时, form_invalid() 还会将先前在表单中填写的值保存到HTML页面中。在上面的实现中,我们可以在重新渲染表单之前检测到并删除 POST 请求中的 csrfmiddlewaretoken 值。然后,我们将先前的值作为上下文变量传递到表单中的输入字段中。

参考文档:

  1. https://docs.djangoproject.com/en/3.2/ref/class-based-views/mixins-forms/#django.views.generic.edit.FormMixin.form_invalid
  2. https://docs.djangoproject.com/en/3.2/topics/forms/#rendering-form-errors