Formularios con Flask-WTF
Lo primero que hay que hacer en una aplicación de Flask antes de usar
cualquier extensión es instalarla en el sistema, en mi caso dentro del entorno
virtual que creado para el proyecto.
Si estas usando un entorno virtual, actívalo, y una vez dentro ejecuta:
pip install Flask-WTF
En todo este capitulo vamos a trabajar con los archivos que creamos en el
capitulo anterior, donde creamos una calculadora de dos números con las
operaciones básicas. Tendremos que hacer unas serie de cambios en los archivos
para adaptarlos al uso de Flask-WTF.
Al usar Flask-Wtf todo el formulario se representa a través de una clase. Esta
clase hereda del objeto FlaskForm y en ella se definen los campos del
formulario como variables de clase.
1º ) En el directorio de trabajo de
nuestra aplicación vamos a crear un nuevo archivo llamado
forms.py al mismo nivel que
inicio.py. Este fichero recogerá todos los formularios de nuestra aplicación:
Como el código de los ejemplos va siendo de un tamaño más grande voy a
dejar un enlace al final de cada entrada con el código del capítulo.
Comenzamos importando todas las librerías que necesitaremos.
Primeramente importamos la librería FlaskForm la cual
por dependencia nos va a instalar wtforms. Déspues de
wtforms importamos todos los campos del formulario que vayamos a
necesitar y por último también de wtform importamos los
validadores que necesitaremos para validar los formularios.
Como ves, hemos creado una clase "formulario_calculadora" que hereda
de la clase FlaskForm. Además cada uno de los campos del formulario
se ha definido como una variable de clase de un tipo especifico.
Primer_numero y segundo_numero son del tipo IntegerField y la variable de
clase operación es del tipo SelectField, por ejemplo. Esto lo que hace
es que posteriormente el campo se renderice correctamente en la plantilla en
función del tipo con lo que hayamos declarado, es decir Flask va a programar
por nosotros las etiquetas html5, label e input, que hayamos definido para
cada campo.
Aqui puedes encontrar alguno de los módulos que están definidos en
wtform.( https://wtforms.readthedocs.io/en/latest/fields/)
Entre otros tenemos:
BooleanField - Representa una única casilla de selección. Puede estar
seleccionada o no.
DateField - Control para introducir una fecha. Por defecto el formado
es de año, mes y dia. Sin embargo se puede cambiar. Por
ejemplo DateField("Etiqueta para el campo", format='%d-%m-%Y'). Lo que
haría que una fecha valida fuera 02-08-2020
DecimalField - Es un campo de texto que muestra y fuerza a que los
datos sean decimales. Tiene varios parámeteros que se pueden utilizar, entre
otros:
-
places = Cuantos decimales se usarán en el formulario. Por defecto se usan
dos decimales.
- rounding = Como se redondea el decimal.
EmailField - Se suele utilizar a través de un campo StringField. No
obstante para usar el validador "Email", previamente hay que
importar el módulo email-validator con la instrucción:
pip install email-validator
FileFiled - Representa un campo de carga de archivos. Por defecto, su valor será el
nombre del archivo enviado en los datos del formulario.
FloatField - Es un campo de texto en el que todas las entradas se
convierten en un número de coma flotante. Las entradas erróneas se ignoran y
no se aceptan como valor. En la mayor parte de los casos es preferible usar
DecimalField.
HiddenField - Es un campo oculto.
IntegerField - Es un campo de texto, excepto que todas las entradas se convierten
en un número entero.La entrada errónea se ignora y no se acepta como
valor.
MultipleFileField - Como el anterior pero permite escoger
múltiples archivos.
PasswordField - Campo donde introducir la contraseña. Lo que se
teclea aparece por defecto en pantalla como puntos.
RadioField - representan varias casillas de selección (radio
buttons)
SelectField - representa una lista desplegable donde se escoge una
opción.
SelectMultipleField - Igual que el anterior con la la diferencia de
que en la lista desplegable puedes seleccionar multiples elementos.
StringField - Campo de texto de línea simple. Los saltos de
línea son eliminados automáticamente del valor introducido.
SubmitField - Botón que envía el formulario.
TextField - Es un campo de texto del tipo StringField.
TextAreaField - Este campo representa un campo de texto pero
que puede usar varias lineas de texto.
Puedes encontrar más información sobre los diferentes campos de la etiqueta
input de html 5 en
Una vez creada la clase, definir un campo para el formulario es tan sencillo
como teclear:
Nombre de la variable de clase (nombre_del_campo) = TipoDeCampo(propiedades
o atributos)
en nuestro ejemplo:
primer_numero = IntegerField("Número 1",
validators=[DataRequired("Tienes que introducir un número entero")])
primer_numero es la
variable de clase con la que hemos definido el
nombre del campo para
nosotros en nuestro formulario.
IntegerField representa el
tipo de campo
que usaremos. Es un campo de texto que al validarse solo admite números
enteros. Si alguien teclea una letra en vez de un número no se acepta como
valor válido.
El primer parámetro que le pasamos al campo "Numero 1" es el nombre con el que
se mostrará al usuario, es decir la etiqueta <label> del campo en HTML.
- para comprobar que los campos obligatorios no se dejan vacíos
DataRequired() o InputRequired(). A no ser que exista una razón especifica se recomienda usar esta última.
- para especificar la longitud de los mismos usaremos Length(min=valor, max=varlor).
- para comprobar que la estructura de un correo electrónico sea válida además de usar el campo EmailField podemos usar el validador Email(). Eso si, como ya dije tienes que instalarlo previamente con pip install email-validator.
- para verificar cuando se registra una contraseña que esta es correcta se suele utilizar un segundo campo para que se vuelva a teclear. Se puede utilizar el validador EqualTo('nombre del campo a comparar', mensaje=None) para comparar el valor de los dos campos.
- algunos campos que quieres que sean opcionales, hay ocasiones en que tiene que usar el validador Optional() para que te funcionen en el proyecto, ya que si no da error. No vale con no poner nada en los validadores. Este validador permite una entrada vacía y detiene la comprobación del resto de validadores. Si la entrada esta vacía también elimina errores anteriores (como errores de procesamiento) del campo.
Puedes ver unos cuantos en
la documentación. No obstante el propio navegador también valida los campos que definimos si no
especificamos lo contrario con "novalidate" dentro de la etiqueta form.
2) formulario_post.html
El siguiente paso que vamos a realizar es actualizar el contenido de
la plantilla formulario_post.html que realizamos en el cápitulo
anterior para que use la clase formulario_calculadora. El cambio
principal es que la plantilla espera un objeto de la clase
formulario_calculadora, que instanciaremos en la vista correspondiente en
inicio.py, y que hemos llamado form
En la plantilla podemos generar el formulario campo por campo o
también podemos recorrerlo.
Generando el formulario campo por campo.
En el ejemplo de arriba puedes ver como dejamos a flask-Wtf que renderice
la etiqueta (label) de cada campo con {{form.<nombre_del_campo>.label}} y que genere el código de cada campo con {{form.<nombre_del_campo>()}}
Si quieres utilizar alguna de las propiedades de la etiqueta de html5
puedes hacerlo. Por ejemplo si queremos poner la propiedad placeholder, que nos pone un texto apagado para que ayude al usuario a rellenar este
campo teclearíamos:
{{form.nombre_del_campo(placeholder="texto a mostrar")}}
o si por ejemplo se quiere que un determinado campo no se autocomplete por
el navegador usaríamos:
{{form.nombre_del_campo(autocomplete="off")}}
o que tenga un determinado tamaño, lo que también podríamos haber hecho al definir los validadores del campo con Length(min=, max=)
{{form.nombre_del_campo(size=32)}}
Os dejo un enlace de etiquetas html para el campo input y sus
atributos.
Si quieres puedes dejar que el navegador valide los campos por ti, pero para
ver mejor el uso de los validadores en el ejemplo, lo he desactivado añadiendo
el atributo novalidate a la etiqueta del formulario. (Esto es opcional)
Además después de insertar cada campo podrías recorrer el diccionario
errors para mostrar al usuario los posibles errores que pudiera haber
en el mismo.
{% for error in form.nombre_campo.errors %}
<span style="color:
red;">{{ error }}</span>
{% endfor %}
No obstante en el ejemplo he optado por recorrer el diccionario de errores y
mostrar los errores y la etiqueta del campo que lo genera al final del
formulario de la siguiente forma:
{% for field, errors in form.errors.items() %}
<!-- Usamos
",".join(errors) para trasformar la lista en texto
y que no salgan los corchetes por pantalla
-->
{{ form[field].label }}: {{",".join(errors)}}
{% endfor %}
Recorriendo el formulario.
Al utilizar un bucle for recorremos todos los campos del formulario y se
generará la etiqueta y el propio campo.
{% for field in form %}
{{field.label}}{{field}}<br/>
{% endfor %}
Seguridad en los formularios
Si te fijas justo después de la etiqueta form esta definido el siguiente
campo:
{{form.csref_token}}
nota: También cumple la misma función {{form.hidden_tag()}}
Por defecto flask-wtf protege los formularios contra el ataque CSRF
(Cross-Site Request Forgery o
falsificación de petición en sitios cruzados). Resumidamente este ataque se produce cuando un sitio web malicioso
envía solicitudes a un sitio web en el que esta conectada la
víctima.
Para implementar esta protección anti CSRF, Flask-Wtf necesita que
configuremos una clave de cifrado (SECRET_KEY), para poder generar tokens encriptados
que se utilizarán para verificar la autenticidad de la petición. Vamos a ver como implementarla.
3 ) Vista para el formulario formulario_post.html.
Para no complicarnos, en el archivo
inicio.py
vamos a teclear esta instrucción (linea 7):
app.secret_key = 'clave de cifrado lo mas robusta posible'
app.config["SECRET_KEY"]="clave de cifrado (no se la digas a
nadie)"
Esta clave de cifrado, la pones tú y conviene que sea una clave o
contraseña, lo más compleja posible.
Dentro de las bibliotecas standar de python está secrets
que nos facilita esta labor. Puedes obtener un clave aceptable tecleando
directamente en el shell de python
>>> import secrets
>>> secrets.token_hex(20)
'5c4fe301d80bebd89ad0fce78c6c9c1deb6d7667'
Si la contraseña va a formar parte de una url es mejor utilizar
token_urlsafe() ya que esto nos garantiza que los caracteres utilizados son
seguros de utilizar en una url.
>>> import secrets
>>> secrets.token_urlsafe(20)
'bbehSBYtWCFRfNOeFETwPm44fSE'
>>>
Seguimos viendo los cambios hechos en la vista del archivo inicio.py.
Lo primero que hemos hecho es, como siempre, importar las bibliotecas de
Flask necesarias para ejecutar la aplicación, pero además tenemos que
importar la clase formulario_calculadora que hemos creado.
línea 3:
from forms import formulario_calculadora
y a continuación podemos instanciar o crear un objeto a partir de ella en la vista
en la que la necesitemos.
línea 13:
form = formulario_calculadora()
El objeto form nos ofrece atributos y métodos para su gestión:
form.validate_on_submit |
Nos permite comprobar si el formulario ha sido enviado y es
válido.
|
form.data |
Nos ofrece un diccionario con los datos del formulario. |
form.errors |
Si el formulario no es válido nos devuelve un diccionario con
los errores.
|
form.primer_numero.data |
Para cada campo (primer_numero es este ejemplo) nos devuelve su valor.
|
form.primer_numero.errors |
Es una tupla con los errores de validación del campo determinado.
|
form.primer_campo |
Nos devuelve el código html para generar ese campo. |
form.primer_numero.label |
Nos devuelve el código html para generar la etiqueta del campo.
|
Puedes encontrar más atributos y métodos en la documentación de
WTforms
Enviando y gestionando la información del formulario.
-
La primera vez que acedamos a la ruta lo haremos usando el método GET.
Como en nuestro caso el formulario ni se ha validado ni enviado todavía,
la intrucción "if form.validate_on_submit()" será falsa y no se
ejecutará. Esto hará que se ejecute el ELSE siguiente, renderizando la
plantilla formulario_post.html con los campos del fomulario, form,
en blanco, ya que aún no tienen ningún dato.
-
Ahora si, rellenamos el formulario y le damos al botón de enviar. El
programa lo valida y si es correcto lo envía con el método POST a
la misma ruta.
-
Como "if form.validate_on_submit()" ahora es True, se
gestiona la información enviada por el formulario y se realiza lo que se
tenga que hacer con ella. En nuestro caso calcular un resultado.
-
Para terminar si todo el proceso ha ido bien se volverá a renderizar la
plantilla incluyendo la variable resultado, y si algo ha ido mal se
mostrará el texto "No se ha podido realizar la Operación".
El resultado final es algo parecido a esto:
No hay comentarios:
Publicar un comentario