Validaci贸n de forma de matraz con Flask-WTF

     

    Introducci贸n

    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.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *