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 *