La pieza que decide el HTML, el selector de fechas que sale vacío, y el desplegable que aparece en inglés.
⏱ 15 min de lectura
Un campo de formulario son dos cosas pegadas que conviene separar: qué se acepta y cómo se pregunta. Lo primero es el campo y ya se ha visto entero. Lo segundo es el widget, y hasta ahora ha ido saliendo el que tocaba por defecto.
En cuanto hay un diseño, eso deja de bastar. Y hay dos casos donde no es cosmética sino un fallo de verdad: un selector de fecha que aparece vacío al editar, y un desplegable que sale en inglés en un sitio en español. Los dos están medidos aquí.
texto <input type="text" name="texto" required id="id_texto">
largo <textarea name="largo" cols="40" rows="3" required id="id_largo"></textarea>
clave <input type="password" name="clave" required id="id_clave">
oculto <input type="hidden" name="oculto" id="id_oculto">
fecha <input type="date" name="fecha" required id="id_fecha">
numero <input type="number" name="numero" min="0" max="120" step="1" required id="id_numero">
eleccion <select name="eleccion"><option value="a">Uno</option>...</select>
radio <div id="id_radio"><div><label for="id_radio_0"><input type="radio" ...
varias <div id="id_varias"><div><label for="id_varias_0"><input type="checkbox" ...
si <input type="checkbox" name="si" id="id_si">
El id se genera con el prefijo id_ y el name es el nombre del campo, y eso es lo que permite que la etiqueta apunte al control y que peticion.POST tenga las claves que tiene.
Los que hay que conocer, porque son los que se cambian:
Sirve para cualquier atributo, incluidos los de accesibilidad (aria-describedby), los de comportamiento (autocomplete, inputmode) y los que usan las bibliotecas de JavaScript (data-...). Y fíjate en que se puede cambiar el type de un TextInput, que es la forma corta de conseguir search, tel, url o color sin widget propio.
Poner class widget a widget es insufrible y es justo lo que pide cualquier marco de estilos. La forma correcta es el __init__:
python
classContactoForm(forms.ModelForm):classMeta:
model = Contacto
fields =['nombre','correo','telefono']def__init__(self,*args,**kwargs):super().__init__(*args,**kwargs)for campo inself.fields.values():
campo.widget.attrs.setdefault('class','form-control')
Dos detalles que hacen que esto funcione bien. El super() va primero, porque self.fields no existe hasta que el padre lo construye. Y setdefault en lugar de asignación directa: así un campo que ya tenía su clase la conserva, en vez de perderla.
Ese __init__ es además el sitio de las otras dos cosas que hacen falta a menudo:
python
def__init__(self,*args,**kwargs):self.usuario = kwargs.pop('usuario')# un argumento propiosuper().__init__(*args,**kwargs)self.fields['grupo'].queryset = Grupo.objects.filter(dueno=self.usuario)
El kwargs.popantes del super(), porque el padre no sabe qué es usuario y protestaría. Y limitar el queryset de un campo de relación es lo que resuelve el problema de los desplegables enormes de la lección 26, y también un agujero de seguridad, porque un desplegable sin filtrar enseña los grupos de todo el mundo.
<div class="obligatorio con-error">
<label for="id_a" class="obligatorio">A:</label>
<ul class="errorlist" id="id_a_error"><li>Este campo es obligatorio.</li></ul>
<input type="text" name="a" required ...>
Django añade esas clases al contenedor y a la etiqueta de cada campo según corresponda. Con dos reglas de CSS ya tienes el asterisco de los obligatorios y el borde rojo de los que fallan, y el HTML de la plantilla no cambia.
El del medio es el que se escribe siempre y el navegador lo enseña vacío. Un <input type="date"> solo entiende el valor en AAAA-MM-DD; con cualquier otra cosa, se calla y no pinta nada.
El efecto en producción es de los caros: alguien edita un registro, no ve la fecha, la rellena a ojo o la deja en blanco, y se pierde el dato original. Y no hay ningún error en ninguna parte.
El arreglo es decirle al widget en qué formato tiene que escribir:
python
fecha = forms.DateField(widget=forms.DateInput(attrs={'type':'date'},format='%Y-%m-%d'))