La validación del formulario ocurre cuando se limpian los datos. Si desea personalizar este proceso, hay varios lugares para realizar cambios, cada uno con un propósito diferente. Se ejecutan tres tipos de métodos de limpieza durante el procesamiento del formulario. Estos normalmente se ejecutan cuando llama al is_valid() método en un formulario. Hay otras cosas que también pueden desencadenar la limpieza y la validación (acceder al errors atributo o llamada full_clean() directamente), pero normalmente no serán necesarios.

En general, cualquier método de limpieza puede aumentar ValidationError Si hay un problema con los datos que está procesando, pasar la información relevante al ValidationError constructor. Vea abajo para conocer las mejores prácticas en la crianza ValidationError. Si no ValidationError se genera, el método debe devolver los datos limpios (normalizados) como un objeto Python.

La mayor parte de la validación se puede realizar mediante validadores, ayudantes que se pueden reutilizar. Los validadores son funciones (o invocables) que toman un solo argumento y generan ValidationError en entrada inválida. Los validadores se ejecutan después del campo to_python y validate se han llamado métodos.

La validación de un formulario se divide en varios pasos, que se pueden personalizar o anular:

  • los to_python() método en un Field es el primer paso en cada validación. Coacciona el valor a un tipo de datos correcto y aumenta ValidationError si eso no es posible. Este método acepta el valor sin procesar del widget y devuelve el valor convertido. Por ejemplo, un FloatField convertirá los datos en un Python float o levantar un ValidationError.
  • los validate() método en un Field maneja la validación específica de campo que no es adecuada para un validador. Toma un valor que ha sido coaccionado a un tipo de datos correcto y aumenta ValidationError ante cualquier error. Este método no devuelve nada y no debería alterar el valor. Debe anularlo para manejar la lógica de validación que no puede o no quiere poner en un validador.
  • los run_validators() método en un Field ejecuta todos los validadores del campo y agrega todos los errores en un solo ValidationError. No debería necesitar anular este método.
  • los clean() método en un Field la subclase es responsable de ejecutar to_python(), validate(), y run_validators() en el orden correcto y propagando sus errores. Si, en cualquier momento, alguno de los métodos aumenta ValidationError, la validación se detiene y se genera ese error. Este método devuelve los datos limpios, que luego se insertan en el cleaned_data diccionario de la forma.
  • los clean_() se llama al método en una subclase de formulario, donde se reemplaza con el nombre del atributo de campo de formulario. Este método realiza cualquier limpieza que sea específica de ese atributo en particular, sin relación con el tipo de campo que es. A este método no se le pasa ningún parámetro. Deberá buscar el valor del campo en self.cleaned_data y recuerde que será un objeto Python en este punto, no el original string enviado en el formulario (estará en cleaned_data porque el campo general clean() El método anterior ya ha limpiado los datos una vez).

    Por ejemplo, si desea validar que el contenido de un CharField llamado serialnumber fue único, clean_serialnumber() sería el lugar adecuado para hacer esto. No necesita un campo específico (es un CharField), pero desea una validación específica del campo de formulario y, posiblemente, limpiar / normalizar los datos.

    El valor de retorno de este método reemplaza el valor existente en cleaned_data, por lo que debe ser el valor del campo de cleaned_data (incluso si este método no lo cambió) o un nuevo valor limpiado.

  • La subclase de formulario clean() El método puede realizar una validación que requiere acceso a varios campos de formulario. Aquí es donde puede poner marcas como “si el campo A se suministra, campo B debe contener una dirección de correo electrónico válida ”. Este método puede devolver un diccionario completamente diferente si lo desea, que se utilizará como cleaned_data.

    Dado que los métodos de validación de campo se han ejecutado en el momento clean() se llama, también tiene acceso al formulario errors atributo que contiene todos los errores provocados por la limpieza de campos individuales.

    Tenga en cuenta que cualquier error planteado por su Form.clean() la anulación no se asociará con ningún campo en particular. Entran en un “campo” especial (llamado __all__), a la que puede acceder a través del non_field_errors() método si es necesario. Si desea adjuntar errores a un campo específico en el formulario, debe llamar add_error().

    También tenga en cuenta que hay consideraciones especiales al anular la clean() método de un ModelForm subclase. (ver el Documentación de ModelForm para más información)

Estos métodos se ejecutan en el orden indicado anteriormente, un campo a la vez. Es decir, para cada campo del formulario (en el orden en que se declaran en la definición del formulario), Field.clean() se ejecuta el método (o su anulación), luego clean_(). Finalmente, una vez que esos dos métodos se ejecutan para cada campo, el Form.clean() El método, o su anulación, se ejecuta independientemente de que los métodos anteriores hayan generado errores o no.

A continuación se proporcionan ejemplos de cada uno de estos métodos.

Como se mencionó, cualquiera de estos métodos puede generar un ValidationError. Para cualquier campo, si el Field.clean() el método genera un ValidationError, no se llama a ningún método de limpieza específico del campo. Sin embargo, los métodos de limpieza para todos los campos restantes aún se ejecutan.

Levantamiento ValidationError

Para que los mensajes de error sean flexibles y fáciles de anular, tenga en cuenta las siguientes pautas:

  • Proporcione un error descriptivo code al constructor:

    # Good
    ValidationError(_('Invalid value'), code='invalid')# Bad
    ValidationError(_('Invalid value'))
  • No fuerce variables en el mensaje; utilizar marcadores de posición y el params argumento del constructor:

    # Good
    ValidationError(
        _('Invalid value: %(value)s'),
        params='value':'42',)# Bad
    ValidationError(_('Invalid value: %s')% value)
  • Utilice claves de mapeo en lugar de formato posicional. Esto permite poner las variables en cualquier orden u omitirlas por completo al reescribir el mensaje:

    # Good
    ValidationError(
        _('Invalid value: %(value)s'),
        params='value':'42',)# Bad
    ValidationError(
        _('Invalid value: %s'),
        params=('42',),)
  • Envuelva el mensaje con gettext para habilitar la traducción:

    # Good
    ValidationError(_('Invalid value'))# Bad
    ValidationError('Invalid value')

Poniendolo todo junto:

raise ValidationError(
    _('Invalid value: %(value)s'),
    code='invalid',
    params='value':'42',)

Seguir estas pautas es particularmente necesario si escribe formularios, campos de formulario y campos de modelo reutilizables.

Si bien no se recomienda, si se encuentra al final de la cadena de validación (es decir, su formulario clean() método) y sabes que lo harás Nunca necesita anular su mensaje de error, aún puede optar por el menos detallado:

ValidationError(_('Invalid value: %s')% value)

los Form.errors.as_data() y Form.errors.as_json() los métodos se benefician enormemente de todas las funciones ValidationErrors (con un code nombre y un params diccionario).

Planteando múltiples errores

Si detecta varios errores durante un método de limpieza y desea señalarlos todos al remitente del formulario, es posible pasar una lista de errores al ValidationError constructor.

Como arriba, se recomienda pasar una lista de ValidationError instancias con codearena params pero una lista de cadenas también funcionará:

# Goodraise ValidationError([
    ValidationError(_('Error 1'), code='error1'),
    ValidationError(_('Error 2'), code='error2'),])# Badraise ValidationError([
    _('Error 1'),
    _('Error 2'),])

Usando la validación en la práctica

Las secciones anteriores explicaron cómo funciona la validación en general para formularios. Dado que a veces puede ser más fácil poner las cosas en su lugar al ver cada característica en uso, aquí hay una serie de pequeños ejemplos que usan cada una de las características anteriores.

Usando validadores

Los campos de formulario (y modelo) de Django admiten el uso de funciones y clases de utilidad conocidas como validadores. Un validador es un objeto o función invocable que toma un valor y no devuelve nada si el valor es válido o genera un valor ValidationError que no. Estos se pueden pasar al constructor de un campo, a través del campo validators argumento, o definido en el Field clase en sí misma con el default_validators atributo.

Los validadores se pueden usar para validar valores dentro del campo, echemos un vistazo a Django’s SlugField:

from django.core import validators
from django.forms import CharField

classSlugField(CharField):
    default_validators =[validators.validate_slug]

Como se puede ver, SlugField es un CharField con un validador personalizado que valida que el texto enviado obedece a algunas reglas de caracteres. Esto también se puede hacer en la definición de campo, por lo que:

slug = forms.SlugField()

es equivalente a:

slug = forms.CharField(validators=[validators.validate_slug])

Los casos comunes, como la validación con un correo electrónico o una expresión regular, se pueden manejar utilizando las clases de validación existentes disponibles en Django. Por ejemplo, validators.validate_slug es una instancia de un RegexValidator construido con el primer argumento siendo el patrón: ^[-a-zA-Z0-9_]+$. Consulte la sección sobre validadores de escritura para ver una lista de lo que ya está disponible y un ejemplo de cómo escribir un validador.

Limpieza predeterminada del campo de formulario

Primero creemos un campo de formulario personalizado que valide que su entrada es un string que contiene direcciones de correo electrónico separadas por comas. La clase completa se ve así:

from django import forms
from django.core.validators import validate_email

classMultiEmailField(forms.Field):defto_python(self, value):"""Normalize data to a list of strings."""# Return an empty list if no input was given.ifnot value:return[]return value.split(',')defvalidate(self, value):"""Check if value consists only of valid emails."""# Use the parent's handling of required fields, etc.super().validate(value)for email in value:
            validate_email(email)

Cada formulario que usa este campo ejecutará estos métodos antes de que se pueda hacer cualquier otra cosa con los datos del campo. Se trata de una limpieza específica para este tipo de campo, independientemente de cómo se utilice posteriormente.

Vamos a crear un ContactForm para demostrar cómo usarías este campo:

classContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField()
    sender = forms.EmailField()
    recipients = MultiEmailField()
    cc_myself = forms.BooleanField(required=False)

Usar MultiEmailField como cualquier otro campo de formulario. Cuando el is_valid() se llama al método en el formulario, el MultiEmailField.clean() El método se ejecutará como parte del proceso de limpieza y, a su vez, llamará al método personalizado. to_python() y validate() métodos.

Limpiar un atributo de campo específico

Continuando con el ejemplo anterior, suponga que en nuestro ContactForm, queremos asegurarnos de que recipients el campo siempre contiene la dirección "[email protected]". Esta es la validación que es específica de nuestro formulario, por lo que no queremos ponerla en el general MultiEmailField clase. En cambio, escribimos un método de limpieza que opera en el recipients campo, así:

from django import forms
from django.core.exceptions import ValidationError

classContactForm(forms.Form):# Everything as before....defclean_recipients(self):
        data = self.cleaned_data['recipients']if"[email protected]"notin data:raise ValidationError("You have forgotten about Fred!")# Always return a value to use as the new cleaned data, even if# this method didn't change it.return data

Limpieza y validación de campos que dependen unos de otros

Supongamos que agregamos otro requisito a nuestro formulario de contacto: si el cc_myself el campo es True, los subject debe contener la palabra "help". Estamos realizando la validación en más de un campo a la vez, por lo que el formulario clean() El método es un buen lugar para hacer esto. Nótese que estamos hablando de la clean() método en el formulario aquí, mientras que antes estábamos escribiendo un clean() método en un campo. Es importante mantener clara la diferencia de campo y de forma al determinar dónde validar las cosas. Los campos son puntos de datos únicos, los formularios son una colección de campos.

Para cuando el formulario clean() se llama al método, todos los métodos de limpieza de campo individuales se habrá ejecutado (las dos secciones anteriores), por lo que self.cleaned_data se completará con cualquier dato que haya sobrevivido hasta ahora. Por lo tanto, también debe recordar tener en cuenta el hecho de que los campos que desea validar pueden no haber sobrevivido a las verificaciones de campo individuales iniciales.

Hay dos formas de informar cualquier error de este paso. Probablemente el método más común es mostrar el error en la parte superior del formulario. Para crear un error de este tipo, puede generar un ValidationError desde el clean() método. Por ejemplo:

from django import forms
from django.core.exceptions import ValidationError

classContactForm(forms.Form):# Everything as before....defclean(self):
        cleaned_data =super().clean()
        cc_myself = cleaned_data.get("cc_myself")
        subject = cleaned_data.get("subject")if cc_myself and subject:# Only do something if both fields are valid so far.if"help"notin subject:raise ValidationError("Did not send for 'help' in the subject despite ""CC'ing yourself.")

En este código, si se genera el error de validación, el formulario mostrará un mensaje de error en la parte superior del formulario (normalmente) que describe el problema. Dichos errores son errores que no son de campo, que se muestran en la plantilla con form.non_field_errors .

La llamada a super().clean() en el código de ejemplo asegura que se mantenga cualquier lógica de validación en las clases principales. Si su formulario hereda otro que no devuelve un cleaned_data diccionario en su clean() método (hacerlo es opcional), entonces no asigne cleaned_data al resultado de la super() llamar y usar self.cleaned_data en lugar de:

defclean(self):super().clean()
    cc_myself = self.cleaned_data.get("cc_myself")...

El segundo enfoque para informar errores de validación puede implicar la asignación del mensaje de error a uno de los campos. En este caso, asignemos un mensaje de error a las filas “asunto” y “cc_myself” en la pantalla del formulario. Tenga cuidado al hacer esto en la práctica, ya que puede generar una salida de formulario confusa. Aquí mostramos lo que es posible y dejamos que usted y sus diseñadores determinen qué funciona de manera efectiva en su situación particular. Nuestro nuevo código (que reemplaza la muestra anterior) se ve así:

from django import forms

classContactForm(forms.Form):# Everything as before....defclean(self):
        cleaned_data =super().clean()
        cc_myself = cleaned_data.get("cc_myself")
        subject = cleaned_data.get("subject")if cc_myself and subject and"help"notin subject:
            msg ="Must put 'help' in subject when cc'ing yourself."
            self.add_error('cc_myself', msg)
            self.add_error('subject', msg)

El segundo argumento de add_error() puede ser un string, o preferiblemente una instancia de ValidationError. Ver Aumento de error de validación para más detalles. Tenga en cuenta que add_error() elimina automáticamente el campo de cleaned_data.