Introducci贸n
Contenido
La validaci贸n de formularios es uno de los componentes m谩s esenciales de la entrada de datos en aplicaciones web. Los usuarios pueden cometer errores, algunos usuarios son malintencionados. Con la validaci贸n de entrada, protegemos nuestra aplicaci贸n de datos incorrectos que afectan la l贸gica empresarial y entradas maliciosas destinadas a da帽ar nuestros sistemas.
Intentar procesar entradas de usuario no validadas puede causar errores inesperados / no manejados, si no un bloqueo del servidor. En este contexto, validar datos significa verificar la entrada y verificar si cumple con ciertas expectativas o criterios. La validaci贸n de datos se puede realizar tanto en el front como en el back-end.
En este tutorial, aprenderemos c贸mo validar la entrada del usuario en formularios Flask usando la extensi贸n Flask-WTForms.
Al finalizar este tutorial, tendremos el siguiente formulario de registro de usuario con criterios de validaci贸n:
Usaremos Flask versi贸n 1.1.2 y Flask-WTF con la versi贸n 0.14.3.
Preparar
Si bien no es necesario, le recomendamos que cree un entorno virtual para seguir:
$ mkdir flask-form-validation
$ cd flask-form-validation
$ python3 -m venv .
$ . bin/activate
En su entorno virtual activado, instalaremos nuestros paquetes escribiendo:
$ pip install Flask Flask-WTF
Nota que si desea utilizar la validaci贸n de correo electr贸nico, tambi茅n deber谩 instalar el email_validator
paquete (la versi贸n actual es 1.1.1):
$ pip3 install email_validator
Ahora creemos nuestros archivos necesarios. Empezaremos creando un b谩sico app.py
, que, por simplicidad, contendr谩 nuestra aplicaci贸n Flask, rutas y formularios:
from flask import Flask, render_template
app = Flask(__name__, template_folder=".")
app.config['SECRET_KEY']='LongAndRandomSecretKey'
Creamos un objeto Flask y configuramos template_folder
a la carpeta actual. Luego asignamos el Flask
objeto en app
variable. Agregamos SECRET_KEY
para nuestro app
configuraci贸n del objeto.
los SECRET_KEY
se utiliza com煤nmente para el cifrado con conexiones de base de datos y sesiones de navegador. WTForms utilizar谩 el SECRET_KEY
como una sal para crear un token CSRF. Puede leer m谩s sobre CSRF en esta p谩gina wiki.
Si su aplicaci贸n ya usa el SECRET_KEY
config para otros fines, querr谩 crear uno nuevo para WTForms. En ese caso, puede configurar el WTF_CSRF_SECRET_KEY
config.
Creemos y agreguemos un formulario a nuestro app.py
:
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
class GreetUserForm(FlaskForm):
username = StringField(label=('Enter Your Name:'))
submit = SubmitField(label=('Submit'))
# ...
Nuestro simple GreetUserForm
la clase contiene una StringField
. Como su nombre lo indica, este campo espera y devolver谩 un valor de cadena (siempre puede convertir esa entrada a otros tipos de datos cuando sea necesario). El nombre del campo es username
, y usaremos este nombre para acceder a los datos del elemento de formulario.
los label
Los par谩metros son lo que se mostrar谩 en nuestra p谩gina para que los usuarios comprendan qu茅 datos captura un elemento de formulario. Tambi茅n tenemos un submit
, que intentar谩 enviar el formulario si todos los campos pasan nuestros criterios de validaci贸n.
Ahora que estamos configurados, 隆usemos WTForms para validar nuestros datos!
Validaci贸n de formularios de matraces con Flask-WTForms
Comencemos por crear una ruta para mostrar y procesar nuestro formulario:
# ...
@app.route("https://Pharos.sh.com/", methods=('GET', 'POST'))
def index():
form = GreetUserForm()
if form.validate_on_submit():
return f'''<h1> Welcome {form.username.data} </h1>'''
return render_template('index.html', form=form)
Nuestra ruta tiene GET
y POST
m茅todos. los GET
El m茅todo muestra el formulario, mientras que el POST
El m茅todo procesa los datos del formulario al enviarlo. Establecemos la ruta de la URL en /
, o la URL ra铆z, por lo que aparecer谩 como la p谩gina de inicio de nuestra aplicaci贸n web. Hacemos el index.html
plantilla y pasar la form
objeto como par谩metro.
Hagamos una pausa y prestemos mucha atenci贸n a esta l铆nea: if form.validate_on_submit():
. Esta regla dice ‘si el m茅todo de solicitud es ENVIAR y si los campos del formulario son v谩lidos, contin煤e. Si la entrada de nuestro formulario pasa nuestros criterios de validaci贸n, en la p谩gina siguiente se mostrar谩 un mensaje de bienvenida simple con el nombre del usuario. Observe que aqu铆 usamos el nombre del campo (username
) para acceder a los datos de entrada.
Para ver el formulario, necesitamos crear el index.html
modelo. Cree el archivo y agregue el siguiente c贸digo:
<form method="POST" action="">
<div class="form-row">
<div class="form-group col-md-6">
{{ form.csrf_token() }}
<label for=""> {{ form.username.label }}</label>
{{ form.username }}
</div>
<div class="form-group">
{{ form.submit(class="btn btn-primary")}}
</div>
</div>
</form>
Usamos nuestro form
object para pasar elementos WTform a Jinja2, el analizador de plantillas para Flask.
Nota: Los csrf_token
es generado autom谩ticamente por WTForms y cambia cada vez que se representa la p谩gina. Esto nos ayuda a proteger nuestro sitio contra ataques CSRF. Por defecto, es un campo oculto. Tambi茅n puede optar por utilizar {{ form.hidden_field() }}
para representar todos los campos ocultos, incluido el token CSRF, pero no se recomienda.
Ahora, vayamos a nuestra terminal para iniciar nuestra aplicaci贸n Flask escribiendo:
$ FLASK_ENV=development flask run
Por conveniencia, configuramos el FLASK_ENV
variable de entorno a “desarrollo” durante el desarrollo. Esto permite que la aplicaci贸n se recargue en caliente cada vez que presionamos guardar. Para Windows, es posible que deba usar set FLASK_ENV=development
en su terminal / consola antes de ejecutar su aplicaci贸n de matraz.
Esto es lo que veremos si navegamos al localhost:
Escriba un nombre en el campo de entrada y env铆e el formulario. Ver谩s el mensaje de saludo que definimos en nuestra ruta:
Funciona como se esperaba. Pero, 驴y si no escribimos nada en el campo de entrada? Todav铆a validar铆a el formulario:
Evitemos que eso suceda y solo permitamos que los usuarios que escribieron sus nombres vean la p谩gina siguiente. Para hacerlo, debemos asegurarnos de que nuestro username
el campo tiene datos de entrada.
Importaremos uno de los m茅todos de validaci贸n integrados de WTForms: DataRequired()
desde wtforms.validators
y pasarlo a nuestro username
campo.
# ...
from wtforms.validators import ValidationError, DataRequired
class GreetUserForm(FlaskForm):
username = StringField(label=('Enter Your Name:'),
validators=[DataRequired()])
submit = SubmitField(label=('Submit'))
# ...
Observe que estamos pasando el validators
par谩metro como una lista. Esto nos dice que podemos tener varios validadores para cada campo.
Ahora que estamos usando DataRequired()
, la username
El campo no se validar谩 si no hay datos de entrada:
De hecho, si hacemos clic derecho e inspeccionamos el elemento del formulario, veremos que WTForms agreg贸 autom谩ticamente el required
atributo al campo de entrada:
Al hacerlo, WTForms agrega una validaci贸n b谩sica de front-end a nuestro campo de formulario. No podr铆a enviar ese formulario sin el username
campo incluso si intenta publicar el formulario utilizando herramientas como cURL o Postman.
Ahora, digamos que queremos establecer una nueva regla de validaci贸n que solo permitir谩 nombres que tengan al menos 5 caracteres. Podemos usar el Length()
validador con min
par谩metro:
# ...
from wtforms.validators import ValidationError, DataRequired, Length
class GreetUserForm(FlaskForm):
username = StringField(label=('Enter Your Name:'),
validators=[DataRequired(), Length(min=5)])
submit = SubmitField(label=('Submit'))
# ...
Si intentamos enviar el formulario con datos de entrada de menos de 5 caracteres, no se cumplir谩n los criterios de validaci贸n y el env铆o fallar谩:
Hacer clic en el bot贸n enviar no hace nada por los datos no v谩lidos, tampoco muestra ning煤n error al usuario. Necesitamos proporcionar mensajes de error para que el usuario comprenda qu茅 est谩 pasando y c贸mo solucionarlo.
En nuestro index.html
plantilla, justo debajo de la {{ form.username }}
, agregue el siguiente bucle for Jinja2 para mostrar errores:
{% for field, errors in form.errors.items() %}
<small class="form-text text-muted ">
{{ ', '.join(errors) }}
</small>
{% endfor %}
Nuestro formulario puede generar errores de validaci贸n limpios ahora:
Por cualquier motivo, si necesitamos limitar la longitud m谩xima de nuestros datos de campo, podemos hacerlo pasando el max
par谩metro al Length()
validador. Tambi茅n es posible personalizar el mensaje de error pasando un opcional message
par谩metro con una cadena de error personalizada.
Actualicemos el username
campo en consecuencia:
# ...
class GreetUserForm(FlaskForm):
username = StringField(label=('Enter Your Name:'),
validators=[DataRequired(),
Length(min=5, max=64, message="Name length must be between %(min)d and %(max)dcharacters") ])
submit = SubmitField(label=('Submit'))
# ...
M谩s campos y validadores de WTForms con el formulario de registro de usuario
Nuestro formulario actual tiene un solo campo, que es algo aburrido. WTForms proporciona amplios criterios de validaci贸n de formularios y una variedad de campos de formulario, as铆 que aprovech茅moslo y creemos algo con uso pr谩ctico.
Crearemos un formulario de registro de usuario y usaremos validadores WTForms integrados.
Usaremos el DataRequired()
validador de los campos que queremos asegurarnos de que el usuario los rellene. Comprobaremos la longitud m铆nima y m谩xima de los campos con Length()
validador, validar correos electr贸nicos con Email()
validador y compruebe si dos campos contienen los mismos datos con EqualTo()
validador.
Quitar el GreetUserForm
class y reemplace el comienzo de su c贸digo con nuestro nuevo formulario:
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField,
SubmitField
from wtforms.validators import ValidationError, DataRequired,
Email, EqualTo, Length
class CreateUserForm(FlaskForm):
username = StringField(label=('Username'),
validators=[DataRequired(),
Length(max=64)])
email = StringField(label=('Email'),
validators=[DataRequired(),
Email(),
Length(max=120)])
password = PasswordField(label=('Password'),
validators=[DataRequired(),
Length(min=8, message="Password should be at least %(min)d characters long")])
confirm_password = PasswordField(
label=('Confirm Password'),
validators=[DataRequired(message="*Required"),
EqualTo('password', message="Both password fields must be equal!")])
receive_emails = BooleanField(label=('Receive merketting emails.'))
submit = SubmitField(label=('Submit'))
# ...
Tenemos cuatro campos diferentes en nuestros formularios. El 煤ltimo es un bot贸n de env铆o normal. Nosotros usamos StringField
para obtener la entrada de cadena de los usuarios, como username
y email
. Por otra parte, PasswordField
oculta el texto de la contrase帽a en el front-end. BooleanField
se representa como una casilla de verificaci贸n en la interfaz, ya que solo contiene valores Verdadero (marcado) o Falso (no seleccionado).
Necesitamos modificar index.html
plantilla para representar nuestros nuevos campos de formulario:
<div class="container">
<h2>Registration Form</h2>
{% for field, errors in form.errors.items() %}
{{ ', '.join(errors) }}
{% endfor %}
<form class="form-horizontal" method="POST" action="">
{{ form.csrf_token() }}
<div class="form-group">
{{ form.username.label }}
{{ form.username(class="form-control") }}
</div>
<div class="form-group">
{{ form.email.label }}
{{ form.email(class="form-control") }}
</div>
<div class="form-group">
{{ form.password.label }}
{{ form.password(class="form-control") }}
</div>
<div class="form-group">
{{ form.confirm_password.label }}
{{ form.confirm_password(class="form-control") }}
</div>
<div class="form-group">
{{ form.receive_emails.label }}
</div>
<div class="form-group">
{{ form.submit(class="btn btn-primary")}}
</div>
</form>
</div>
Nuestros campos de formulario se representan correctamente como puede ver:
Nota: Si su sitio web va a tener varios formularios diferentes, es posible que desee utilizar macros Jinja2 en lugar de escribir cada campo de formulario uno por uno. El uso de macros est谩 fuera del alcance de este art铆culo, pero acelera enormemente los procesos de creaci贸n de formularios.
Creaci贸n de sus propios validadores personalizados
En la mayor铆a de los sitios web, no se permiten ciertos caracteres en los nombres de usuario. Puede ser por motivos de seguridad, puede ser para cosm茅ticos. WTForms no tiene esa l贸gica por defecto, pero podemos definirla nosotros mismos.
WTForms nos permite agregar validadores personalizados agregando una validaci贸n m茅todo para nuestro UserRegistrationForm
clase. Implementemos esa validaci贸n personalizada en nuestro formulario agregando el validate_username()
m茅todo justo debajo del submit
bot贸n.
# ... class UserRegistrationForm (FlaskForm): # ... submit = SubmitField (label = ('Submit')) def validate_username (self, username): exclusive_chars = "*?! '^ +% & / () =} ][{$#"
for char in self.username.data:
if char in excluded_chars:
raise ValidationError(
f"Character {char} is not allowed in username.")
# ...
We can add as many or as few validation methods as we like. WTForms will run validation methods automatically once defined.
The ValidationError
class gives us a convenient way to define our custom validation message. Note that you will need to import it from wtforms.validators
before using it.
Let’s test this new method by entering proper data to all fields except the username
field, which will contain an excluded character – ‘%’.
As you can see, our custom validation method runs perfectly and provides us with a clean validation error, which helps us to understand what’s wrong with our input data. Doing so, greatly improves the user experience.
You can use external libraries, your database, or APIs to combine with WTForms and to validate the incoming input data. When you want to capture {{ form.some_field.data }}
and write into or query from the database, use WTForms validators to ensure it’s safe to be saved.
Note: We’ve excluded most of the HTML codes out since they are not directly related to our tutorial. The full code will be available on this GitHub repository, in case you want to check out.
Conclusion
Validating data is one of the most essential parts of the Flask web applications. Flask-WTforms provides very powerful and easy to learn ways to handle form data.
Now that you know the fundamentals of data validation with Flask-WTF, you can go ahead and apply your own validation logic and/or implement your own methods for both security and better user experience.