Nuestro grupo de expertos pasados algunos días de investigación y de recopilar de datos, encontramos la respuesta, deseamos que te resulte útil para tu proyecto.
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 unField
es el primer paso en cada validación. Coacciona el valor a un tipo de datos correcto y aumentaValidationError
si eso no es posible. Este método acepta el valor sin procesar del widget y devuelve el valor convertido. Por ejemplo, unFloatField
convertirá los datos en un Pythonfloat
o levantar unValidationError
. - los
validate()
método en unField
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 aumentaValidationError
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 unField
ejecuta todos los validadores del campo y agrega todos los errores en un soloValidationError
. No debería necesitar anular este método. - los
clean()
método en unField
la subclase es responsable de ejecutarto_python()
,validate()
, yrun_validators()
en el orden correcto y propagando sus errores. Si, en cualquier momento, alguno de los métodos aumentaValidationError
, la validación se detiene y se genera ese error. Este método devuelve los datos limpios, que luego se insertan en elcleaned_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 enself.cleaned_data
y recuerde que será un objeto Python en este punto, no el original string enviado en el formulario (estará encleaned_data
porque el campo generalclean()
El método anterior ya ha limpiado los datos una vez).Por ejemplo, si desea validar que el contenido de un
CharField
llamadoserialnumber
fue único,clean_serialnumber()
sería el lugar adecuado para hacer esto. No necesita un campo específico (es unCharField
), 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 decleaned_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 campoA
se suministra, campoB
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á comocleaned_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 formularioerrors
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 delnon_field_errors()
método si es necesario. Si desea adjuntar errores a un campo específico en el formulario, debe llamaradd_error()
.También tenga en cuenta que hay consideraciones especiales al anular la
clean()
método de unModelForm
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 ValidationError
s (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 code
arena 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
.
Sección de Reseñas y Valoraciones
Eres capaz de añadir valor a nuestra información dando tu veteranía en las crónicas.