La API de Formularios

Sobre este documento

Este documento cubre los detalles sucios de la API de formularios de Django. Deberías leer la introducción a trabajar con formularios primero.

Formularios vinculados y no vinculados

Una instancia de la clase Form es o bien vinculada a un conjunto de datos, o no vinculada.

  • Si está vinculada a un conjunto de datos, puede validar ese conjunto de datos y renderizar el formulario como HTML con los datos mostrados en el HTML.

  • Si está no vinculada, no puede realizar la validación (porque no hay datos para validar!), pero sí puede renderizar el formulario vacío como HTML.

class Form[fuente]

Crear una instancia no vinculada de la clase Form, instanciando la clase:

>>> f = ContactForm()

Para vincular datos a un formulario, pasa los datos como un diccionario como el primer parámetro al constructor de tu clase Form:

>>> data = {
...     "subject": "hello",
...     "message": "Hi there",
...     "sender": "foo@example.com",
...     "cc_myself": True,
... }
>>> f = ContactForm(data)

En este diccionario, las claves son los nombres de campo, que corresponden a los atributos en tu clase Form. Los valores son los datos que estás intentando validar. Normalmente serán cadenas de texto, pero no hay requisito de que sean cadenas; el tipo de datos que pasas depende del Field, como veremos en un momento.

Form.is_bound

Si necesitas distinguir entre instancias de formulario vinculadas y no vinculadas en tiempo de ejecución, verifica el valor del atributo is_bound del formulario.

>>> f = ContactForm()
>>> f.is_bound
False
>>> f = ContactForm({"subject": "hello"})
>>> f.is_bound
True

Nota que pasar un diccionario vacío crea una forma vinculada con datos vacíos:

>>> f = ContactForm({})
>>> f.is_bound
True

Si tienes una instancia de un formulario (Form) vinculada y deseas cambiar los datos de alguna manera, o si quieres vincular a algunos datos una instancia no vinculada (Form), crea otra instancia de formulario (Form). No existe forma de cambiar los datos en una instancia de formulario (Form). Una vez creada la instancia de un formulario (Form), debes considerar sus datos inmutables, ya tenga datos o no.

Validar datos mediante formularios

Form.clean()

Implementa un método clean() en tu Form cuando debes agregar validación personalizada para campos que están interdependientes. Consulta el ejemplo de uso en validando-campos-con-clean.

Form.is_valid()

La tarea principal de un objeto Form es validar datos. Con una instancia de Form vinculada, llama al método is_valid() para ejecutar la validación y devuelve un valor booleano que indica si los datos eran válidos:

>>> data = {
...     "subject": "hello",
...     "message": "Hi there",
...     "sender": "foo@example.com",
...     "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True

Vamos a intentarlo con algunos datos inválidos. En este caso, subject está en blanco (un error, porque todos los campos son obligatorios por defecto) y sender no es una dirección de correo electrónico válida:

>>> data = {
...     "subject": "",
...     "message": "Hi there",
...     "sender": "invalid email address",
...     "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
False
Form.errors

Access the errors attribute to get a dictionary of error messages:

>>> f.errors
{'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}

En este diccionario, las claves son los nombres de los campos y los valores son listas de cadenas que representan los mensajes de error. Los mensajes de error se almacenan en listas porque un campo puede tener múltiples mensajes de error.

Puedes acceder a errors sin tener que llamar a is_valid() primero. Los datos del formulario se validarán la primera vez que llames a is_valid() o accedas a errors.

Las rutinas de validación solo se llamarán una vez, independientemente de cuántas veces accedas a errors o llames a is_valid(). Esto significa que si la validación tiene efectos laterales, esos efectos solo se desencadenarán una vez.

Form.errors.as_data()

Devuelve un dict que mapea campos a sus instancias originales de ValidationError.

>>> f.errors.as_data()
{'sender': [ValidationError(['Enter a valid email address.'])],
'subject': [ValidationError(['This field is required.'])]}

Utiliza este método en cualquier momento en que necesites identificar un error por su code. Esto permite cosas como reescribir el mensaje del error o escribir lógica personalizada en una vista cuando un error dado esté presente. También se puede utilizar para serializar los errores en un formato personalizado (por ejemplo, XML); por ejemplo, as_json() depende de as_data().

La necesidad del método as_data() se debe a la compatibilidad hacia atrás. Anteriormente las instancias de ValidationError se perdían tan pronto como sus mensajes de error renderizados se agregaban al diccionario Form.errors. Idealmente Form.errors debería haber almacenado instancias de ValidationError y métodos con un prefijo as_ podrían renderizarlos, pero tuvo que hacerse la otra forma para no romper el código que espera mensajes de error renderizados en Form.errors.

Form.errors.as_json(escape_html=False)

Devuelve los errores serializados como JSON.

>>> f.errors.as_json()
{"sender": [{"message": "Enter a valid email address.", "code": "invalid"}],
"subject": [{"message": "This field is required.", "code": "required"}]}

Por defecto, as_json() no escape su salida. Si estás utilizando esto para algo como solicitudes AJAX a una vista del formulario donde el cliente interpreta la respuesta y inserta errores en la página, asegúrate de escapar los resultados en el lado del cliente para evitar la posibilidad de un ataque de inyección de código cruzado. Puedes hacerlo en JavaScript con element.textContent = errorText o con jQuery’s $(el).text(errorText) (en lugar de su función .html()).

Si por alguna razón no quieres utilizar la escapada del lado del cliente, también puedes establecer escape_html=True y los mensajes de error se escaparán para que puedas usarlos directamente en HTML.

Form.errors.get_json_data(escape_html=False)

Returns the errors as a dictionary suitable for serializando a JSON. Form.errors.as_json() devuelve JSON serializado, mientras que este método devuelve los datos de error antes de que se serialicen.

El parámetro escape_html comporta como se describe en Form.errors.as_json().

Form.add_error(field, error)

Este método permite agregar errores a campos específicos desde dentro del método Form.clean() o desde fuera de la forma en sí; por ejemplo, desde una vista.

El argumento field es el nombre del campo al que se deben agregar los errores. Si su valor es None, el error se tratará como un error no relacionado con un campo tal y como lo devuelve Form.non_field_errors().

El argumento error puede ser una cadena, o preferiblemente una instancia de ValidationError. Consulta Levantar ValidationError para obtener prácticas recomendadas sobre cómo definir errores de formulario.

Nota que Form.add_error() elimina automáticamente el campo relevante de cleaned_data.

Form.has_error(field, code=None)

Este método devuelve un booleano designando si un campo tiene un error con un código específico de error. Si code es None, devolverá True si el campo contiene cualquier error en absoluto.

Para comprobar errores no relacionados con campos utilice NON_FIELD_ERRORS como parámetro field.

Form.non_field_errors()

Este método devuelve la lista de errores desde Form.errors que no están asociadas a un campo en particular. Esto incluye ValidationErrors que se levantan en Form.clean() y errores agregados utilizando Form.add_error(None, "...").

Comportamiento de formas no vinculadas

It’s meaningless to validate a form with no data, but, for the record, here’s what happens with unbound forms:

>>> f = ContactForm()
>>> f.is_valid()
False
>>> f.errors
{}

Los valores iniciales de la forma

Form.initial

Utiliza el atributo initial para declarar el valor inicial de los campos de la forma en tiempo de ejecución. Por ejemplo, podrías querer rellenar un campo username con el nombre de usuario de la sesión actual.

Para lograr esto, utiliza el argumento initial a un Form. Este argumento, si se proporciona, debería ser un diccionario que mapee nombres de campos a valores iniciales. Solo incluye los campos para los cuales estás especificando un valor inicial; no es necesario incluir todos los campos de tu forma. Por ejemplo:

>>> f = ContactForm(initial={"subject": "Hi there!"})

Estos valores solo se muestran en formas sin datos y no se utilizan como valores por defecto si no se proporciona un valor particular.

Si un campo Field define el atributo initial y incluyes el atributo initial al instanciar la Form, entonces el último initial tendrá precedencia. En este ejemplo, initial se proporciona tanto a nivel de campo como a nivel de instancia de forma, y el último tiene precedencia:

>>> from django import forms
>>> class CommentForm(forms.Form):
...     name = forms.CharField(initial="class")
...     url = forms.URLField()
...     comment = forms.CharField()
...
>>> f = CommentForm(initial={"name": "instance"}, auto_id=False)
>>> print(f)
<div>Name:<input type="text" name="name" value="instance" required></div>
<div>Url:<input type="url" name="url" required></div>
<div>Comment:<input type="text" name="comment" required></div>
Form.get_initial_for_field(field, field_name)

Devuelve los datos iniciales para un campo de formulario. Recupera los datos del atributo Form.initial si está presente, de lo contrario intenta con Field.initial. Los valores llamables se evalúan.

Se recomienda utilizar el atributo BoundField.initial sobre la función get_initial_for_field() porque BoundField.initial tiene una interfaz más simple. Además, a diferencia de la función get_initial_for_field(), el atributo BoundField.initial almacena sus valores en caché. Esto es útil especialmente cuando se tratan con llamables cuyos valores pueden cambiar (por ejemplo datetime.now o uuid.uuid4):

>>> import uuid
>>> class UUIDCommentForm(CommentForm):
...     identifier = forms.UUIDField(initial=uuid.uuid4)
...
>>> f = UUIDCommentForm()
>>> f.get_initial_for_field(f.fields["identifier"], "identifier")
UUID('972ca9e4-7bfe-4f5b-af7d-07b3aa306334')
>>> f.get_initial_for_field(f.fields["identifier"], "identifier")
UUID('1b411fab-844e-4dec-bd4f-e9b0495f04d0')
>>> # Using BoundField.initial, for comparison
>>> f["identifier"].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
>>> f["identifier"].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')

Comprobando qué datos de formulario han cambiado

Form.has_changed()

Utiliza el método has_changed() en tu Form cuando necesites comprobar si los datos del formulario han cambiado desde los datos iniciales.

>>> data = {
...     "subject": "hello",
...     "message": "Hi there",
...     "sender": "foo@example.com",
...     "cc_myself": True,
... }
>>> f = ContactForm(data, initial=data)
>>> f.has_changed()
False

Cuando se envía el formulario, lo reconstruimos y proporcionamos los datos originales para que la comparación pueda realizarse:

>>> f = ContactForm(request.POST, initial=data)
>>> f.has_changed()

La función has_changed() será True si los datos de request.POST difieren de lo que se proporcionó en initial o False en caso contrario. El resultado se calcula llamando a la función Field.has_changed() para cada campo del formulario.

Form.changed_data

La propiedad changed_data devuelve una lista con los nombres de los campos cuyos valores en los datos vinculados al formulario (generalmente request.POST) difieren de lo que se proporcionó en initial. Devuelve una lista vacía si no hay diferencias.

>>> f = ContactForm(request.POST, initial=data)
>>> if f.has_changed():
...     print("The following fields changed: %s" % ", ".join(f.changed_data))
...
>>> f.changed_data
['subject', 'message']

Acceder a los campos del formulario

Form.fields

Puedes acceder a los campos de la instancia de Form desde su propiedad fields:

>>> for row in f.fields.values():
...     print(row)
...
<django.forms.fields.CharField object at 0x7ffaac632510>
<django.forms.fields.URLField object at 0x7ffaac632f90>
<django.forms.fields.CharField object at 0x7ffaac3aa050>
>>> f.fields["name"]
<django.forms.fields.CharField object at 0x7ffaac6324d0>

Puedes alterar el campo y la clase BoundField de la instancia de Form para cambiar la forma en que se presenta en el formulario:

>>> f.as_div().split("</div>")[0]
'<div><label for="id_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_subject">'
>>> f["subject"].label = "Topic"
>>> f.as_div().split("</div>")[0]
'<div><label for="id_subject">Topic:</label><input type="text" name="subject" maxlength="100" required id="id_subject">'

Ten cuidado de no alterar la propiedad base_fields porque esta modificación influirá en todos los formularios ContactForm posteriores dentro del mismo proceso Python:

>>> f.base_fields["subject"].label_suffix = "?"
>>> another_f = ContactForm(auto_id=False)
>>> another_f.as_div().split("</div>")[0]
'<div><label for="id_subject">Subject?</label><input type="text" name="subject" maxlength="100" required id="id_subject">'

Acceder a datos «limpios»

Form.cleaned_data

Cada campo en una clase Form es responsable no sólo de validar los datos, sino también de «limpiarlos» – normalizarlos a un formato consistente. Esto es una característica agradable porque permite que los datos para un campo particular se ingresen de varias maneras, siempre resultando en una salida consistente.

Por ejemplo, la clase DateField normaliza el input en un objeto datetime.date de Python. Independientemente de si le pasas una cadena en formato '1994-07-15', un objeto datetime.date, o varios otros formatos, DateField siempre normalizará a un objeto datetime.date siempre y cuando sea válido.

Una vez que hayas creado una instancia de Form con un conjunto de datos y lo hayas validado, puedes acceder a los datos limpios mediante su atributo cleaned_data:

>>> data = {
...     "subject": "hello",
...     "message": "Hi there",
...     "sender": "foo@example.com",
...     "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}

Ten en cuenta que cualquier campo de texto – como CharField o EmailField – siempre limpia la entrada en una cadena. Cubriremos las implicaciones de codificación más adelante en este documento.

Si tus datos no se validan, el diccionario cleaned_data contiene solo los campos válidos:

>>> data = {
...     "subject": "",
...     "message": "Hi there",
...     "sender": "invalid email address",
...     "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
False
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there'}

cleaned_data siempre contendrá solo una clave para los campos definidos en la Form, incluso si pasas datos adicionales cuando defines la Form. En este ejemplo, pasamos un montón de campos adicionales al constructor de ContactForm, pero cleaned_data contiene solo los campos de la forma:

>>> data = {
...     "subject": "hello",
...     "message": "Hi there",
...     "sender": "foo@example.com",
...     "cc_myself": True,
...     "extra_field_1": "foo",
...     "extra_field_2": "bar",
...     "extra_field_3": "baz",
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data  # Doesn't contain extra_field_1, etc.
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}

Cuando la Form es válida, cleaned_data incluirá una clave y valor para todos sus campos, incluso si el diccionario de datos no incluía un valor para algunos campos opcionales. En este ejemplo, el diccionario de datos no incluye un valor para el campo nick_name, pero cleaned_data lo incluye, con un valor vacío:

>>> from django import forms
>>> class OptionalPersonForm(forms.Form):
...     first_name = forms.CharField()
...     last_name = forms.CharField()
...     nick_name = forms.CharField(required=False)
...
>>> data = {"first_name": "John", "last_name": "Lennon"}
>>> f = OptionalPersonForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'nick_name': '', 'first_name': 'John', 'last_name': 'Lennon'}

En el ejemplo anterior, el valor de cleaned_data para nick_name se establece en una cadena vacía, porque nick_name es CharField, y los campos CharField tratan los valores vacíos como una cadena vacía. Cada tipo de campo sabe qué es su «valor en blanco» – por ejemplo, para DateField, es None en lugar de la cadena vacía. Para obtener detalles completos sobre el comportamiento de cada campo en este caso, consulte la nota sobre «Valor vacío» para cada campo en la sección Clases de campo integradas Field a continuación.

Puedes escribir código para realizar validaciones para campos formularios específicos (basados en su nombre) o para la forma como un todo (considerando combinaciones de varios campos). Más información sobre esto se encuentra en Validación de formularios y campos.

Imprimir formas como HTML

La segunda tarea de un objeto Form es renderizarlo como HTML. Para hacerlo, imprímelo:

>>> f = ContactForm()
>>> print(f)
<div><label for="id_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_subject"></div>
<div><label for="id_message">Message:</label><input type="text" name="message" required id="id_message"></div>
<div><label for="id_sender">Sender:</label><input type="email" name="sender" required id="id_sender"></div>
<div><label for="id_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_cc_myself"></div>

Si la forma está ligada a datos, el salida en HTML incluirá esos datos apropiadamente. Por ejemplo, si un campo se representa por un <input type="text">, los datos estarán en el atributo value. Si un campo se representa por un <input type="checkbox">, entonces ese HTML incluirá checked si corresponde:

>>> data = {
...     "subject": "hello",
...     "message": "Hi there",
...     "sender": "foo@example.com",
...     "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> print(f)
<div><label for="id_subject">Subject:</label><input type="text" name="subject" value="hello" maxlength="100" required id="id_subject"></div>
<div><label for="id_message">Message:</label><input type="text" name="message" value="Hi there" required id="id_message"></div>
<div><label for="id_sender">Sender:</label><input type="email" name="sender" value="foo@example.com" required id="id_sender"></div>
<div><label for="id_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_cc_myself" checked></div>

Este es el texto traducido:

  • Por flexibilidad, el output no incluye las etiquetas <form> y </form> ni una etiqueta <input type="submit">. Es tu trabajo hacerlo.

  • Cada tipo de campo tiene una representación HTML predeterminada. El campo CharField se representa con un <input type="text"> y el campo EmailField con un <input type="email">. El campo BooleanField(null=False) se representa con un <input type="checkbox">. Toma nota de que estos son solo valores predeterminados sensatos; puedes especificar qué HTML utilizar para un campo dado utilizando widgets, lo que explicaremos más adelante.

  • El nombre HTML del etiqueta se toma directamente del nombre de atributo en la clase ContactForm.

  • El texto de etiqueta para cada campo – por ejemplo, 'Asunto:', 'Mensaje:' y 'Copia a mismo:' se genera desde el nombre del campo convirtiendo todos los guiones bajos a espacios y mayusculando la primera letra. Toma nota nuevamente de que estos son solo valores predeterminados sensatos; también puedes especificar etiquetas manualmente.

  • Cada texto de etiqueta está rodeado por una etiqueta HTML <label>, que apunta al campo de formulario correspondiente a través de su id. Su id, a su vez, se genera prestando 'id_' al nombre del campo. Los atributos id y las etiquetas <label> están incluidos en el output por defecto para seguir las mejores prácticas, pero puedes cambiar ese comportamiento.

  • El output utiliza la sintaxis HTML5, dirigida a <!DOCTYPE html>. Por ejemplo, utiliza atributos booleanos como checked en lugar del estilo XHTML de checked='checked'.

Aunque el output <div> es el estilo de salida predeterminado cuando imprimes un formulario puedes personalizar el output utilizando tu propio template de formulario que puede ser configurado a nivel de sitio, por formulario o por instancia. Consulta Plantillas de formulario reutilizables.

Renderizado predeterminado

El renderizado predeterminado cuando imprimes un formulario utiliza los siguientes métodos y atributos.

template_name

Form.template_name

El nombre del template renderizado si el formulario se convierte en una cadena, por ejemplo, mediante print(form) o en un template mediante {{ form }}.

Por defecto, una propiedad que devuelve el valor de la plantilla del renderer form_template_name. Puedes establecerlo como nombre de plantilla de cadena para superponer ese valor para una clase de formulario en particular.

render()

Form.render(template_name=None, context=None, renderer=None)

El método render se llama tanto por __str__ como los métodos Form.as_div(), Form.as_table(), Form.as_p() y Form.as_ul(). Todos los argumentos son opcionales y tienen el valor predeterminado:

Al pasar template_name puedes personalizar la plantilla utilizada para una sola llamada.

get_context()

Form.get_context()

Return the contexto de la plantilla para renderizar el formulario.

El contexto disponible es:

  • form: El formulario vinculado.

  • fields: Todos los campos vinculados, excepto los campos ocultos.

  • hidden_fields: Todos los campos vinculados ocultos.

  • errors: Todas las errores del formulario no relacionados con el campo o ocultos.

template_name_label

Form.template_name_label

La plantilla utilizada para renderizar un campo <label>, utilizado cuando se llama a BoundField.label_tag()/legend_tag(). Puede cambiarse por formulario mediante la sobrescritura de esta propiedad o más generalmente mediante la sobrescripción de la plantilla predeterminada, consulte también Sobreescribiendo plantillas de formulario predeterminadas.

Estilos de salida

La forma recomendada para cambiar el estilo de salida del formulario es establecer una plantilla personalizada para el formulario, ya sea a nivel de sitio, por formulario o por instancia. Consulte Plantillas de formulario reutilizables para ejemplos.

Los siguientes textos traducidos manteniendo todas sus etiquetas intactas:

Nota

De las plantillas y estilos de salida proporcionados por el framework, se recomienda el as_div() por defecto sobre las versiones as_p(), as_table(), y as_ul() ya que la plantilla implementa <fieldset> y <legend> para agrupar inputs relacionados y es más fácil de navegar para usuarios de lectores de pantalla.

Cada par de ayuda asocia un método de formulario con una atributo dando el nombre de plantilla apropiado.

as_div()

Form.template_name_div

La plantilla utilizada por as_div(). Por defecto: 'django/forms/div.html'.

Form.as_div()

as_div() renderiza el formulario como una serie de elementos <div>, con cada <div> conteniendo un campo, tal que:

>>> f = ContactForm()
>>> f.as_div()

… da HTML como:

<div>
<label for="id_subject">Subject:</label>
<input type="text" name="subject" maxlength="100" required id="id_subject">
</div>
<div>
<label for="id_message">Message:</label>
<input type="text" name="message" required id="id_message">
</div>
<div>
<label for="id_sender">Sender:</label>
<input type="email" name="sender" required id="id_sender">
</div>
<div>
<label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself">
</div>

as_p()

Form.template_name_p

La plantilla utilizada por as_p(). Por defecto: 'django/forms/p.html'.

Form.as_p()

as_p() renderiza el formulario como una serie de etiquetas <p>, con cada <p> conteniendo un campo:

>>> f = ContactForm()
>>> f.as_p()
'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" required></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>'
>>> print(f.as_p())
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>
<p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></p>
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>

as_ul()

Form.template_name_ul

El template utilizado por as_ul(). Por defecto: 'django/forms/ul.html'.

Form.as_ul()

as_ul() renderiza la forma como una serie de etiquetas <li> , con cada <li> conteniendo un campo. No incluye las etiquetas <ul> o </ul>, para que puedas especificar cualquier atributo HTML en la etiqueta <ul> para mayor flexibilidad:

>>> f = ContactForm()
>>> f.as_ul()
'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>\n<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>'
>>> print(f.as_ul())
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>
<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>

as_table()

Form.template_name_table

El template utilizado por as_table(). Por defecto: 'django/forms/table.html'.

Form.as_table()

as_table() renderiza la forma como una tabla HTML <table>:

>>> f = ContactForm()
>>> f.as_table()
'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>'
>>> print(f.as_table())
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>

Estilización de filas y campos formularios requeridos o erróneos

Form.error_css_class
Form.required_css_class

Es bastante común estilar las filas y campos formularios que son requeridos o tienen errores. Por ejemplo, podrías querer presentar las filas formularios requeridas en negrita y destacar los errores en rojo.

La clase Form tiene un par de trampas que puedes usar para agregar atributos class a las filas requeridas o a las filas con errores: establece las atributos Form.error_css_class y/o Form.required_css_class:

from django import forms


class ContactForm(forms.Form):
    error_css_class = "error"
    required_css_class = "required"

    # ... and the rest of your fields here

Una vez que hayas hecho eso, las filas recibirán clases "error" y/o "required" , según sea necesario. El HTML será algo como:

>>> f = ContactForm(data)
>>> print(f)
<div class="required"><label for="id_subject" class="required">Subject:</label> ...
<div class="required"><label for="id_message" class="required">Message:</label> ...
<div class="required"><label for="id_sender" class="required">Sender:</label> ...
<div><label for="id_cc_myself">Cc myself:</label> ...
>>> f["subject"].label_tag()
<label class="required" for="id_subject">Subject:</label>
>>> f["subject"].legend_tag()
<legend class="required" for="id_subject">Subject:</legend>
>>> f["subject"].label_tag(attrs={"class": "foo"})
<label for="id_subject" class="foo required">Subject:</label>
>>> f["subject"].legend_tag(attrs={"class": "foo"})
<legend for="id_subject" class="foo required">Subject:</legend>

Puedes personalizar la representación de las filas del formulario utilizando un campo BoundField personalizado.

La configuración de los atributos HTML id y las etiquetas <label> de los elementos formularios.

Form.auto_id

Por defecto, los métodos de representación del formulario incluyen:

  • Atributos HTML id en los elementos formularios.

  • Etiquetas <label> correspondientes alrededor de las etiquetas. Una etiqueta HTML <label> designa qué texto de etiqueta está asociado con cuál elemento del formulario. Esta pequeña mejora hace que los formularios sean más usables y accesibles a dispositivos asistentes. Es siempre una buena idea utilizar etiquetas <label>.

Los valores de los atributos id se generan prestando el prefijo id_ a los nombres de los campos del formulario. Este comportamiento es configurable, aunque si deseas cambiar la convención de los atributos id o eliminar las etiquetas <label> y los atributos id por completo.

Utiliza el argumento auto_id del constructor de la clase Form para controlar el comportamiento de los atributos id y las etiquetas. Este argumento debe ser True, False o una cadena.

Si auto_id es False, entonces la salida del formulario no incluirá etiquetas <label> ni atributos id:

>>> f = ContactForm(auto_id=False)
>>> print(f)
<div>Subject:<input type="text" name="subject" maxlength="100" required></div>
<div>Message:<textarea name="message" cols="40" rows="10" required></textarea></div>
<div>Sender:<input type="email" name="sender" required></div>
<div>Cc myself:<input type="checkbox" name="cc_myself"></div>

Si auto_id está configurado en True, entonces la salida del formulario incluye etiquetas <label> y utiliza el nombre del campo como su valor id para cada campo del formulario:

>>> f = ContactForm(auto_id=True)
>>> print(f)
<div><label for="subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="subject"></div>
<div><label for="message">Message:</label><textarea name="message" cols="40" rows="10" required id="message"></textarea></div>
<div><label for="sender">Sender:</label><input type="email" name="sender" required id="sender"></div>
<div><label for="cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="cc_myself"></div>

Si auto_id se configura con una cadena que contenga el carácter de formato '%s', entonces la salida del formulario incluirá etiquetas <label>, y generará atributos id basados en la cadena de formato. Por ejemplo, para una cadena de formato 'field_%s', un campo llamado subject obtendrá el valor id 'field_subject'. Continuando con nuestro ejemplo:

>>> f = ContactForm(auto_id="id_for_%s")
>>> print(f)
<div><label for="id_for_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_for_subject"></div>
<div><label for="id_for_message">Message:</label><textarea name="message" cols="40" rows="10" required id="id_for_message"></textarea></div>
<div><label for="id_for_sender">Sender:</label><input type="email" name="sender" required id="id_for_sender"></div>
<div><label for="id_for_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_for_cc_myself"></div>

Si auto_id se establece en cualquier otro valor verdadero – como una cadena que no incluye %s – entonces la biblioteca actuará como si auto_id fuera True.

Por defecto, auto_id está configurado para la cadena 'id_%s'.

Form.label_suffix

Una cadena translatable (por defecto un dos puntos (:) en inglés) que se agregará después de cualquier nombre de etiqueta cuando una forma se renderice.

Es posible personalizar ese carácter, o omitirlo completamente, utilizando el parámetro label_suffix:

>>> f = ContactForm(auto_id="id_for_%s", label_suffix="")
>>> print(f)
<div><label for="id_for_subject">Subject</label><input type="text" name="subject" maxlength="100" required id="id_for_subject"></div>
<div><label for="id_for_message">Message</label><textarea name="message" cols="40" rows="10" required id="id_for_message"></textarea></div>
<div><label for="id_for_sender">Sender</label><input type="email" name="sender" required id="id_for_sender"></div>
<div><label for="id_for_cc_myself">Cc myself</label><input type="checkbox" name="cc_myself" id="id_for_cc_myself"></div>
>>> f = ContactForm(auto_id="id_for_%s", label_suffix=" ->")
>>> print(f)
<div><label for="id_for_subject">Subject -&gt;</label><input type="text" name="subject" maxlength="100" required id="id_for_subject"></div>
<div><label for="id_for_message">Message -&gt;</label><textarea name="message" cols="40" rows="10" required id="id_for_message"></textarea></div>
<div><label for="id_for_sender">Sender -&gt;</label><input type="email" name="sender" required id="id_for_sender"></div>
<div><label for="id_for_cc_myself">Cc myself -&gt;</label><input type="checkbox" name="cc_myself" id="id_for_cc_myself"></div>

Ten en cuenta que la sufija de etiqueta se agrega solo si el último carácter de la etiqueta no es un carácter de puntuación (en inglés, son ., !, ? o :).

Los campos también pueden definir su propia label_suffix. Esto tendrá prioridad sobre Form.label_suffix. La sufija también se puede sobrescribir en tiempo de ejecución utilizando el parámetro label_suffix a la función label_tag()/ legend_tag().

Form.use_required_attribute

Cuando se establece en True (el valor por defecto), los campos formularios requeridos tendrán la atributo HTML required.

Formsets instancian formas con use_required_attribute=False para evitar la validación incorrecta del navegador cuando se agregan y eliminan formas de un conjunto de formas.

Configurando la renderización de los widgets de una forma

Form.default_renderer

Specifica el renderer a utilizar para la forma. Por defecto, None que significa utilizar el renderer por defecto especificado por la FORM_RENDERER configuración.

Puedes establecer esto como un atributo de clase cuando declares tu formulario o utiliza el argumento renderer en Form.__init__(). Por ejemplo:

from django import forms


class MyForm(forms.Form):
    default_renderer = MyRenderer()

o:

form = MyForm(renderer=MyRenderer())

Notas sobre la ordenación de los campos

En las atajadas as_p(), as_ul() y as_table(), se muestran los campos en el orden en que los defines en tu clase de formulario. Por ejemplo, en el ejemplo del formulario ContactForm, los campos están definidos en el orden subject, message, sender, cc_myself. Para reordenar la salida HTML, cambia el orden en que se enumeran esos campos en la clase.

Hay varias otras formas de personalizar la orden:

Form.field_order

Por defecto Form.field_order=None, lo que mantiene el orden en que defines los campos en tu clase de formulario. Si field_order es una lista de nombres de campos, se ordenan los campos según la lista especificada y los campos restantes se agregan según el orden por defecto. Los nombres de campo desconocidos en la lista se ignoran. Esto permite deshabilitar un campo en una subclase estableciéndolo a None sin tener que redefinir la ordenación.

También puedes utilizar el argumento Form.field_order para sobreescribir la orden de los campos al instanciar el formulario. Si un formulario Form define field_order y incluyes field_order cuando instancias el formulario, entonces el último field_order tendrá prioridad.

Form.order_fields(field_order)

Puedes reorganizar los campos en cualquier momento utilizando order_fields() con una lista de nombres de campo como en field_order.

Cómo se muestran los errores

Si renderizas un objeto de formulario vinculado, el acto de renderizado ejecutará automáticamente la validación del formulario si no ha sucedido ya y la salida HTML incluirá los errores de validación como <ul class="errorlist">.

El siguiente:

>>> data = {
...     "subject": "",
...     "message": "Hi there",
...     "sender": "invalid email address",
...     "cc_myself": True,
... }
>>> ContactForm(data).as_div()

… da HTML como:

<div>
  <label for="id_subject">Subject:</label>
  <ul class="errorlist" id="id_subject_error"><li>This field is required.</li></ul>
  <input type="text" name="subject" maxlength="100" required aria-invalid="true" aria-describedby="id_subject_error" id="id_subject">
</div>
<div>
  <label for="id_message">Message:</label>
  <textarea name="message" cols="40" rows="10" required id="id_message">Hi there</textarea>
</div>
<div>
  <label for="id_sender">Sender:</label>
  <ul class="errorlist" id="id_sender_error"><li>Enter a valid email address.</li></ul>
  <input type="email" name="sender" value="invalid email address" maxlength="320" required aria-invalid="true" aria-describedby="id_sender_error" id="id_sender">
</div>
<div>
    <label for="id_cc_myself">Cc myself:</label>
    <input type="checkbox" name="cc_myself" id="id_cc_myself" checked>
</div>

Los plantillas de formularios predeterminadas de Django asociarán errores de validación con su entrada utilizando la atributo HTML aria-describedby cuando el campo tenga un auto_id y no se proporcione una descripción personalizada aria-describedby. Si se establece una descripción personalizada aria-describedby al definir el widget, esto sobrescribirá el valor predeterminado.

Si el widget se renderiza en un <fieldset> entonces aria-describedby se agrega a este elemento, de lo contrario se agrega al elemento HTML del widget (por ejemplo, <input>).

Se agregó aria-describedby para asociar errores con su entrada.

Personalizando la lista de errores

class ErrorList(initlist=None, error_class=None, renderer=None, field_id=None)[fuente]

Por defecto, las formas utilizan django.forms.utils.ErrorList para formatear los errores de validación. ErrorList es un objeto similar a una lista donde initlist es la lista de errores. Además, esta clase tiene las siguientes atributos y métodos.

La argumento field_id se agregó.

error_class

Las clases CSS a utilizar cuando se renderiza la lista de errores. Cualquier clase proporcionada se agrega a la clase por defecto errorlist.

renderer

Especifica el revisor a utilizar para ErrorList. Por defecto, es None, lo que significa utilizar el revisor por defecto especificado por la configuración de la FORM_RENDERER configuración.

field_id

Un id para el campo al que se relacionan los errores. Esto permite agregar un atributo id HTML en la plantilla de error y es útil para asociar los errores con el campo. La plantilla predeterminada utiliza el formato id="{{ field_id }}_error" y un valor se proporciona mediante Form.add_error() utilizando el auto_id del campo.

template_name

El nombre del template utilizado cuando se llama a __str__ o render(). Por defecto, este es 'django/forms/errors/list/default.html' que es un proxy para el template 'ul.html'.

template_name_text

El nombre del template utilizado cuando se llama a as_text(). Por defecto, este es 'django/forms/errors/list/text.html'. Este template renderiza los errores como una lista de puntos suspensivos.

template_name_ul

El nombre del template utilizado cuando se llama a as_ul(). Por defecto, este es 'django/forms/errors/list/ul.html'. Este template renderiza los errores en etiquetas <li> con un contenedor <ul> y las clases CSS definidas por error_class.

get_context()[fuente]

Devuelve contexto para la renderización de errores en un template.

El contexto disponible es:

  • errors: Una lista de los errores.

  • error_class: Un string de clases CSS.

render(template_name=None, context=None, renderer=None)

El método render se llama tanto por __str__ como por el método as_ul().

Todos los argumentos son opcionales y se establecerán en:

as_text()

Rendere la lista de errores utilizando el template definido por template_name_text.

as_ul()

Rendere la lista de errores utilizando el template definido por template_name_ul.

Si deseas personalizar la representación de los errores, esto se puede lograr sobrescribiendo la atributo template_name o más generalmente sobrescribiendo el template predeterminado, consulte también Sobreescribiendo plantillas de formulario predeterminadas.

Mayor salida detallada

Los métodos as_p(), as_ul() y as_table() son atajos – no son la única forma en que un objeto de formulario puede ser mostrado.

class BoundField[fuente]

Se utiliza para mostrar HTML o acceder a atributos para un campo individual de una instancia Form.

El método __str__() de este objeto muestra el HTML para este campo.

Puedes utilizar Form.bound_field_class y Field.bound_field_class para especificar una clase diferente BoundField por formulario o por campo, respectivamente.

Consulte Customizando BoundField para ejemplos de sobrescritura de un BoundField.

Para recuperar un solo BoundField, utiliza la sintaxis de búsqueda en el diccionario sobre tu formulario utilizando el nombre del campo como clave:

>>> form = ContactForm()
>>> print(form["subject"])
<input id="id_subject" type="text" name="subject" maxlength="100" required>

Para recuperar todos los objetos BoundField, itera sobre el formulario:

>>> form = ContactForm()
>>> for boundfield in form:
...     print(boundfield)
...
<input id="id_subject" type="text" name="subject" maxlength="100" required>
<input type="text" name="message" id="id_message" required>
<input type="email" name="sender" id="id_sender" required>
<input type="checkbox" name="cc_myself" id="id_cc_myself">

El output específico del campo respeta la configuración auto_id del objeto de formulario:

>>> f = ContactForm(auto_id=False)
>>> print(f["message"])
<input type="text" name="message" required>
>>> f = ContactForm(auto_id="id_%s")
>>> print(f["message"])
<input type="text" name="message" id="id_message" required>

Atributos de BoundField

BoundField.aria_describedby[fuente]

Devuelve una referencia aria-describedby para asociar un campo con su texto de ayuda y errores. Devuelve None si aria-describedby está establecido en Widget.attrs para preservar la atributo definido por el usuario cuando se renderiza el formulario.

BoundField.auto_id[fuente]

El atributo ID HTML para este BoundField. Devuelve una cadena vacía si Form.auto_id es False.

BoundField.data[fuente]

Esta propiedad devuelve los datos para este BoundField extraídos por el método value_from_datadict() del widget, o None si no se le dio:

>>> unbound_form = ContactForm()
>>> print(unbound_form["subject"].data)
None
>>> bound_form = ContactForm(data={"subject": "My Subject"})
>>> print(bound_form["subject"].data)
My Subject
BoundField.errors[fuente]

Un objeto que admite listas como la de formato de lista de errores que se muestra como un <ul class="errorlist"> HTML cuando se imprime:

>>> data = {"subject": "hi", "message": "", "sender": "", "cc_myself": ""}
>>> f = ContactForm(data, auto_id=False)
>>> print(f["message"])
<input type="text" name="message" required aria-invalid="true">
>>> f["message"].errors
['This field is required.']
>>> print(f["message"].errors)
<ul class="errorlist"><li>This field is required.</li></ul>
>>> f["subject"].errors
[]
>>> print(f["subject"].errors)

>>> str(f["subject"].errors)
''

Cuando se renderiza un campo con errores, se establecerá aria-invalid="true" en el widget del campo para indicar a los usuarios de lectores de pantalla que hay un error.

BoundField.field

El instante de la clase Field del formulario que este BoundField envuelve.

BoundField.form

La instancia de Form a la que está ligada esta BoundField.

BoundField.help_text

El texto de ayuda del campo, definido por el atributo help_text.

BoundField.html_name

El nombre que se utilizará en el atributo name HTML del widget. Considera la forma prefix.

BoundField.id_for_label[fuente]

Utiliza esta propiedad para renderizar el ID de este campo. Por ejemplo, si estás construyendo manualmente un <label> en tu plantilla (a pesar de que label_tag()/legend_tag() lo harán por ti):

<label for="{{ form.my_field.id_for_label }}">...</label>{{ my_field }}

Por defecto, esto será el nombre del campo prefijado con id_id_my_field» para el ejemplo anterior). Puedes modificar el ID estableciendo attrs en el widget del campo. Por ejemplo, declarando un campo de esta forma:

my_field = forms.CharField(widget=forms.TextInput(attrs={"id": "myFIELD"}))

y utilizando la plantilla anterior, se renderizaría algo como:

<label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" required>
BoundField.initial[fuente]

Utiliza BoundField.initial para recuperar los datos iniciales de un campo de formulario. Recupera los datos del atributo Form.initial si están presentes, o intenta con el atributo Field.initial. Los valores llamables se evalúan. Consulta la referencia Los valores iniciales de la forma para más ejemplos.

BoundField.initial almacena su valor de retorno, lo cual es útil especialmente cuando se tratan con llamables cuyos valores pueden cambiar (por ejemplo datetime.now o uuid.uuid4):

>>> from datetime import datetime
>>> class DatedCommentForm(CommentForm):
...     created = forms.DateTimeField(initial=datetime.now)
...
>>> f = DatedCommentForm()
>>> f["created"].initial
datetime.datetime(2021, 7, 27, 9, 5, 54)
>>> f["created"].initial
datetime.datetime(2021, 7, 27, 9, 5, 54)

Se recomienda utilizar BoundField.initial en lugar de get_initial_for_field().

BoundField.is_hidden[fuente]

Devuelve True si el widget de esta BoundField está oculto.

BoundField.label

El nombre de la etiqueta del campo: label de este campo. Se utiliza en label_tag()/legend_tag().

BoundField.name

El nombre de este campo en el formulario:

>>> f = ContactForm()
>>> print(f["subject"].name)
subject
>>> print(f["message"].name)
message
BoundField.template_name[fuente]

El nombre del template renderizado con BoundField.as_field_group().

Una propiedad que devuelve el valor de la template_name si está configurado, o la field_template_name.

BoundField.use_fieldset[fuente]

Devuelve el valor del atributo use_fieldset de este widget de BoundField.

BoundField.widget_type[fuente]

Devuelve el nombre de clase en minúsculas del widget del campo envuelto, eliminando cualquier tramo de input o widget. Esto puede usarse cuando se están creando formularios donde la disposición depende del tipo de widget. Por ejemplo:

{% for field in form %}
    {% if field.widget_type == 'checkbox' %}
        # render one way
    {% else %}
        # render another way
    {% endif %}
{% endfor %}

Métodos de BoundField

BoundField.as_field_group()

Renderea el campo utilizando BoundField.render() con valores por defecto que renderizan el BoundField, incluyendo su etiqueta, texto de ayuda y errores usando el template’s template_name si está configurado o la field_template_name.

BoundField.as_hidden(attrs=None, **kwargs)[fuente]

Devuelve una cadena de HTML para representar esto como un <input type="hidden">.

Se pasan **kwargs a as_widget().

Este método se utiliza principalmente internamente. Debes utilizar un widget en su lugar.

BoundField.as_widget(widget=None, attrs=None, only_initial=False)[fuente]

Rendere el campo mediante la renderización del widget pasado, agregando cualquier atributo HTML pasado como attrs. Si no se especifica ningún widget, se utilizará el widget predeterminado del campo.

Se utiliza only_initial internamente en Django y no debe establecerse explícitamente.

BoundField.css_classes(extra_classes=None)[fuente]

Cuando utilizas los atajos de renderizado de Django, se utilizan clases CSS para indicar campos de formulario requeridos o que contienen errores. Si estás renderizando manualmente un formulario, puedes acceder a estas clases CSS utilizando el método css_classes:

>>> f = ContactForm(data={"message": ""})
>>> f["message"].css_classes()
'required'

Si deseas proporcionar algunas clases adicionales además de las clases de error y requeridas que pueden ser necesarias, puedes proporcionar esas clases como argumento.

>>> f = ContactForm(data={"message": ""})
>>> f["message"].css_classes("foo bar")
'foo bar required'
BoundField.get_context()[fuente]

Devuelve el contexto del template para renderizar el campo. El contexto disponible es field siendo la instancia del campo vinculado.

BoundField.label_tag(contents=None, attrs=None, label_suffix=None, tag=None)[fuente]

Rendere una etiqueta de label para el campo de formulario utilizando el template especificado por Form.template_name_label.

El contexto disponible es:

  • field: Esta instancia de la clase BoundField.

  • contents: Por defecto, una cadena concatenada de BoundField.label y Form.label_suffix (o Field.label_suffix, si está establecido). Esto se puede sobrescribir mediante los argumentos contents y label_suffix.

  • attrs: Un diccionario que contiene for, Form.required_css_class, y id. id se genera por el widget del campo attrs o BoundField.auto_id. Se pueden proporcionar atributos adicionales mediante el argumento ``attrs””.

  • use_tag: Un booleano que es True si la etiqueta tiene un id. Si False el template por defecto omite el tag.

  • tag: Una cadena opcional para personalizar el tag, con valor por defecto de label.

Truco

En tu plantilla field es la instancia de la clase BoundField. Por lo tanto field.field accede a la propiedad BoundField.field que es el campo que declaras, por ejemplo forms.CharField.

Para renderizar separadamente la etiqueta del tag de un campo de formulario, puedes llamar al método label_tag():

>>> f = ContactForm(data={"message": ""})
>>> print(f["message"].label_tag())
<label for="id_message">Message:</label>

Si deseas personalizar la renderización esto se puede lograr sobreescribiendo la propiedad Form.template_name_label o más generalmente sobreescribiendo el template por defecto, ver también Sobreescribiendo plantillas de formulario predeterminadas.

BoundField.legend_tag(contents=None, attrs=None, label_suffix=None)[fuente]

Llama al método label_tag() con tag='legend' para renderizar la etiqueta con etiquetas <legend>. Esto es útil cuando se están renderizando widgets de radio y múltiples casillas de verificación donde <legend> puede ser más apropiado que una <label>.

BoundField.render(template_name=None, context=None, renderer=None)

El método render es llamado por as_field_group. Todos los argumentos son opcionales y tienen valor por defecto:

Al pasar template_name puedes personalizar la plantilla utilizada para una sola llamada.

BoundField.value()[fuente]

Utiliza este método para renderizar el valor bruto de este campo como lo haría un Widget:

>>> initial = {"subject": "welcome"}
>>> unbound_form = ContactForm(initial=initial)
>>> bound_form = ContactForm(data={"subject": "hi"}, initial=initial)
>>> print(unbound_form["subject"].value())
welcome
>>> print(bound_form["subject"].value())
hi

Customizando BoundField

Form.bound_field_class

Define una clase personalizada BoundField para utilizar al renderizar el formulario. Esto tiene prioridad sobre la clase de campo definida en el proyecto (BaseRenderer.bound_field_class, junto con un FORM_RENDERER personalizado), pero puede ser sobrescrita por la clase de campo individual (Field.bound_field_class).

Si no se define como variable de clase, bound_field_class se puede establecer mediante el argumento bound_field_class en el constructor de Form o Field.

Por razones de compatibilidad, un campo de formulario personalizado todavía puede sobrescribir la método Field.get_bound_field() para utilizar una clase personalizada, aunque cualquier de las opciones anteriores es preferible.

Es posible que desee utilizar una clase personalizada BoundField si necesita acceder a información adicional sobre un campo de formulario en una plantilla y utilizar una subclase de Field no es suficiente.

Por ejemplo, si tiene un GPSCoordinatesField, y quiere poder acceder a información adicional sobre las coordenadas en una plantilla, esto podría implementarse de la siguiente manera:

class GPSCoordinatesBoundField(BoundField):
    @property
    def country(self):
        """
        Return the country the coordinates lie in or None if it can't be
        determined.
        """
        value = self.value()
        if value:
            return get_country_from_coordinates(value)
        else:
            return None


class GPSCoordinatesField(Field):
    bound_field_class = GPSCoordinatesBoundField

Ahora puede acceder al país en una plantilla con {{ form.coordinates.country }}.

También puede querer personalizar el renderizado predeterminado del campo de formulario. Por ejemplo, puede sobrescribir BoundField.label_tag() para agregar un clase CSS:

class StyledLabelBoundField(BoundField):
    def label_tag(self, contents=None, attrs=None, label_suffix=None, tag=None):
        attrs = attrs or {}
        attrs["class"] = "wide"
        return super().label_tag(contents, attrs, label_suffix, tag)


class UserForm(forms.Form):
    bound_field_class = StyledLabelBoundField
    name = CharField()

Esto actualizaría el renderizado de formularios por defecto:

>>> f = UserForm()
>>> print(f["name"].label_tag)
<label for="id_name" class="wide">Name:</label>

Para agregar una clase CSS al elemento HTML que envuelve a todos los campos, se puede sobrescribir la clase BoundField para devolver una colección diferente de clases CSS:

class WrappedBoundField(BoundField):
    def css_classes(self, extra_classes=None):
        parent_css_classes = super().css_classes(extra_classes)
        return f"field-class {parent_css_classes}".strip()


class UserForm(forms.Form):
    bound_field_class = WrappedBoundField
    name = CharField()

Esta es la traducción de los textos originales:

>>> f = UserForm()
>>> print(f)
<div class="field-class"><label for="id_name">Name:</label><input type="text" name="name" required id="id_name"></div>

Alternativamente, para sobreescribir la clase BoundField a nivel de proyecto, se puede definir BaseRenderer.bound_field_class en un FORM_RENDERER personalizado:

mysite/renderers.py
from django.forms.renderers import DjangoTemplates

from .forms import CustomBoundField


class CustomRenderer(DjangoTemplates):
    bound_field_class = CustomBoundField
settings.py
FORM_RENDERER = "mysite.renderers.CustomRenderer"

Asociar archivos subidos a un formulario

Trabajar con formularios que tienen campos FileField y ImageField es ligeramente más complicado que un formulario normal.

Primero, para poder subir archivos, debes asegurarte de que el elemento <form> defina correctamente la propiedad enctype como "multipart/form-data"

<form enctype="multipart/form-data" method="post" action="/foo/">

Segundo, cuando utilices el formulario, necesitarás vincular los datos de archivo. Los datos de archivo se manejan por separado a los datos del formulario normal, por lo que cuando tu formulario contenga un campo FileField y ImageField, deberás especificar un segundo argumento al vincular tu formulario. Así que si extendemos nuestro ContactForm para incluir un campo ImageField llamado mugshot, necesitarás vincular los datos de archivo que contienen la imagen del mugshot:

# Bound form with an image field
>>> from django.core.files.uploadedfile import SimpleUploadedFile
>>> data = {
...     "subject": "hello",
...     "message": "Hi there",
...     "sender": "foo@example.com",
...     "cc_myself": True,
... }
>>> file_data = {"mugshot": SimpleUploadedFile("face.jpg", b"file data")}
>>> f = ContactFormWithMugshot(data, file_data)

En la práctica, generalmente especificarás request.FILES como fuente de datos de archivo (al igual que utilizas request.POST como fuente de datos del formulario):

# Bound form with an image field, data from the request
>>> f = ContactFormWithMugshot(request.POST, request.FILES)

La construcción de un formulario no vinculado es lo mismo que siempre – omite tanto los datos del formulario como los datos de archivo:

# Unbound form with an image field
>>> f = ContactFormWithMugshot()

Prueba para formularios multipart

Form.is_multipart()

Si estás escribiendo vistas o plantillas reutilizables, puede que no sepas con anticipación si tu formulario es un formulario multipartito o no. El método is_multipart() te dice si el formulario requiere codificación multipartita para la presentación:

>>> f = ContactFormWithMugshot()
>>> f.is_multipart()
True

Aquí tienes un ejemplo de cómo podrías utilizar esto en una plantilla:

{% if form.is_multipart %}
    <form enctype="multipart/form-data" method="post" action="/foo/">
{% else %}
    <form method="post" action="/foo/">
{% endif %}
{{ form }}
</form>

Sobrescribiendo formularios

Si tienes varios clases Form que comparten campos, puedes usar sobrecarga para eliminar la redundancia.

Cuando sobrescribes una clase de formulario personalizada, la clase derivada resultante incluirá todos los campos de la clase padre(s), seguidos de los campos que defines en la clase derivada.

En este ejemplo, ContactFormWithPriority contiene todos los campos de ContactForm, más un campo adicional, priority. Los campos de ContactForm están ordenados primero:

>>> class ContactFormWithPriority(ContactForm):
...     priority = forms.CharField()
...
>>> f = ContactFormWithPriority(auto_id=False)
>>> print(f)
<div>Subject:<input type="text" name="subject" maxlength="100" required></div>
<div>Message:<textarea name="message" cols="40" rows="10" required></textarea></div>
<div>Sender:<input type="email" name="sender" required></div>
<div>Cc myself:<input type="checkbox" name="cc_myself"></div>
<div>Priority:<input type="text" name="priority" required></div>

Es posible sobrescribir múltiples formularios, tratando a los formularios como mixins. En este ejemplo, BeatleForm sobrescribe tanto PersonForm como InstrumentForm (en ese orden), y su lista de campos incluye los campos de las clases padre:

>>> from django import forms
>>> class PersonForm(forms.Form):
...     first_name = forms.CharField()
...     last_name = forms.CharField()
...
>>> class InstrumentForm(forms.Form):
...     instrument = forms.CharField()
...
>>> class BeatleForm(InstrumentForm, PersonForm):
...     haircut_type = forms.CharField()
...
>>> b = BeatleForm(auto_id=False)
>>> print(b)
<div>First name:<input type="text" name="first_name" required></div>
<div>Last name:<input type="text" name="last_name" required></div>
<div>Instrument:<input type="text" name="instrument" required></div>
<div>Haircut type:<input type="text" name="haircut_type" required></div>

Es posible declarar con claridad eliminar un campo Field heredado de una clase padre estableciendo el nombre del campo a None en la clase derivada. Por ejemplo:

>>> from django import forms

>>> class ParentForm(forms.Form):
...     name = forms.CharField()
...     age = forms.IntegerField()
...

>>> class ChildForm(ParentForm):
...     name = None
...

>>> list(ChildForm().fields)
['age']

Prefijos para formularios

Form.prefix

Puedes poner varios formularios Django dentro de uno solo <form> etiqueta. Para dar a cada formulario su propio espacio de nombres, utiliza el argumento de palabra clave prefix:

>>> mother = PersonForm(prefix="mother")
>>> father = PersonForm(prefix="father")
>>> print(mother)
<div><label for="id_mother-first_name">First name:</label><input type="text" name="mother-first_name" required id="id_mother-first_name"></div>
<div><label for="id_mother-last_name">Last name:</label><input type="text" name="mother-last_name" required id="id_mother-last_name"></div>
>>> print(father)
<div><label for="id_father-first_name">First name:</label><input type="text" name="father-first_name" required id="id_father-first_name"></div>
<div><label for="id_father-last_name">Last name:</label><input type="text" name="father-last_name" required id="id_father-last_name"></div>

El prefijo también se puede especificar en la clase de formulario:

>>> class PersonForm(forms.Form):
...     ...
...     prefix = "person"
...