Cómo utilizar formularios web en una aplicación de Flask

El autor seleccionó el Free and Open Source Fund para recibir una donación como parte del programa Write for DOnations.

Introducción

Los formularios web, como los campos de texto y las áreas de texto, permiten a los usuarios enviar datos a tu aplicación para utilizarlos para realizar una acción o para enviar áreas de texto más grandes a la aplicación. Por ejemplo, en una aplicación de redes sociales, podrías proporcionar a los usuarios un cuadro donde puedan agregar nuevo contenido a sus páginas. Otro ejemplo es una página de inicio de sesión, donde proporcionarías al usuario un campo de texto para ingresar su nombre de usuario y un campo de contraseña para ingresar su contraseña. El servidor (tu aplicación Flask en este caso) utiliza los datos que el usuario envía y los usa para iniciar sesión si los datos son válidos, o responde con un mensaje como Credenciales inválidas! para informar al usuario que los datos que envió no son correctos.

Flask es un framework web ligero de Python que proporciona herramientas y características útiles para crear aplicaciones web en el lenguaje Python. En este tutorial, construirás una pequeña aplicación web que demuestra cómo usar formularios web. La aplicación tendrá una página para mostrar mensajes que se almacenan en una lista de Python, y una página para agregar nuevos mensajes. También utilizarás mensajes flash para informar a los usuarios de un error cuando envíen datos inválidos.

Requisitos previos

Paso 1 — Mostrar Mensajes

En este paso, crearás una aplicación Flask con una página de índice para mostrar mensajes que se almacenan en una lista de diccionarios de Python.

Primero abre un nuevo archivo llamado app.py para editar:

  1. nano app.py

Añade el siguiente código dentro del archivo app.py para crear un servidor Flask con una única ruta:

flask_app/app.py
from flask import Flask, render_template

app = Flask(__name__)

messages = [{'title': 'Message One',
             'content': 'Message One Content'},
            {'title': 'Message Two',
             'content': 'Message Two Content'}
            ]

@app.route('/')
def index():
    return render_template('index.html', messages=messages)

Guarda y cierra el archivo.

En este archivo, primero importas la clase Flask y la función render_template() desde el paquete flask. Luego usas la clase Flask para crear una nueva instancia de aplicación llamada app, pasando la variable especial __name__, que es necesaria para que Flask configure algunas rutas detrás de escena. La renderización de plantillas se cubre en el tutorial Cómo Usar Plantillas en una Aplicación Flask.

Luego creas una lista global de Python llamada messages, que contiene diccionarios de Python. Cada diccionario tiene dos claves: title para el título del mensaje, y content para el contenido del mensaje. Este es un ejemplo simplificado de un método de almacenamiento de datos; en un escenario del mundo real, usarías una base de datos que guarda los datos de manera permanente y te permite manipularlos de manera más eficiente.

Después de crear la lista en Python, utilizas el decorador @app.route() para crear una función de vista llamada index(). En ella, retornas una llamada a la función render_template(), que indica a Flask que la ruta debe mostrar una plantilla HTML. Nombras esta plantilla index.html (la crearás más adelante) y pasas una variable llamada messages a ella. Esta variable contiene la lista messages que declaraste previamente como valor y la hace disponible para la plantilla HTML. Las funciones de vista se tratan en el tutorial Cómo Crear Tu Primera Aplicación Web Usando Flask y Python 3.

A continuación, crea una carpeta llamada templates en tu directorio flask_app donde Flask busca las plantillas, luego abre un archivo de plantilla llamado base.html, que tendrá código que otras plantillas heredarán para evitar la repetición de código:

  1. mkdir templates
  2. nano templates/base.html

Agrega el siguiente código dentro del archivo base.html para crear la plantilla base con una barra de navegación y un bloque de contenido:

flask_app/templates/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %} - FlaskApp</title>
    <style>
        .message {
            padding: 10px;
            margin: 5px;
            background-color: #f3f3f3
        }
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }

    </style>
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">FlaskApp</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% block content %} {% endblock %}
    </div>
</body>
</html>

Guarda y cierra el archivo.

Esta plantilla base contiene todo el código HTML reutilizable que necesitarás en tus otras plantillas. El bloque title será reemplazado para establecer un título para cada página, y el bloque content será reemplazado con el contenido de cada página. La barra de navegación tiene dos enlaces, uno para la página de índice donde utilizas la función auxiliar url_for() para enlazar a la función de vista index(), y el otro para una página Acerca de si decides incluir una en tu aplicación.

A continuación, abre una plantilla llamada index.html. Esta es la plantilla a la que haces referencia en el archivo app.py:

  1. nano templates/index.html

Agrega el siguiente código a ella:

flask_app/templates/index.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Messages {% endblock %}</h1>
    {% for message in messages %}
        <div class='message'>
            <h3>{{ message['title'] }}</h3>
            <p>{{ message['content'] }}</p>
        </div>
    {% endfor %}
{% endblock %}

Guarda y cierra el archivo.

En este código, extiendes la plantilla base.html y reemplazas el contenido del bloque content. Utilizas un encabezado <h1> que también sirve como título.

Utilizas un bucle for de Jinja en la línea {% for message in messages %} para recorrer cada mensaje en la lista messages. Utilizas una etiqueta <div> para contener el título y el contenido del mensaje. Muestras el título en un encabezado <h3> y el contenido en una etiqueta <p>.

Mientras estás en tu directorio flask_app con tu entorno virtual activado, informa a Flask sobre la aplicación (app.py en este caso) utilizando la variable de entorno FLASK_APP:

  1. export FLASK_APP=app

Luego, establece la variable de entorno FLASK_ENV en development para ejecutar la aplicación en modo de desarrollo y tener acceso al depurador. Para obtener más información sobre el depurador de Flask, consulta Cómo Manejar Errores en una Aplicación Flask. Utiliza los siguientes comandos para hacerlo (en Windows, usa set en lugar de export):

  1. export FLASK_ENV=development

A continuación, ejecuta la aplicación:

  1. flask run

Con el servidor de desarrollo en funcionamiento, visita la siguiente URL utilizando tu navegador:

http://127.0.0.1:5000/

Verás los mensajes en la lista messages mostrados en la página de índice:

Ahora que has configurado tu aplicación web y mostrado los mensajes, necesitarás una forma de permitir que los usuarios agreguen nuevos mensajes a la página de índice. Esto se hace a través de formularios web, que configurarás en el siguiente paso.

Paso 2 — Configuración de Formularios

En este paso, crearás una página en tu aplicación que permita a los usuarios agregar nuevos mensajes a la lista de mensajes a través de un formulario web.

Deja el servidor de desarrollo en funcionamiento y abre una nueva ventana de terminal.

Primero, abre tu archivo app.py:

  1. nano app.py

Agrega la siguiente ruta al final del archivo:

flask_app/app.py
# ...

@app.route('/create/', methods=('GET', 'POST'))
def create():
    return render_template('create.html')

Guarda y cierra el archivo.

Esta ruta /create tiene el parámetro methods con la tupla ('GET', 'POST') para aceptar tanto solicitudes GET como POST. GET y POST son métodos HTTP. Por defecto, solo se aceptan solicitudes GET, que se utilizan para recuperar datos, como solicitar una página de índice o una página Acerca de. Las solicitudes POST se utilizan para enviar datos a una ruta específica, lo que a menudo cambia los datos en el servidor.

En este ejemplo, solicitarás la página create usando una solicitud GET. La página Create tendrá un formulario web con campos de entrada y un botón Submit. Cuando un usuario complete el formulario web y haga clic en el botón Submit, se enviará una solicitud POST a la ruta /create. Allí manejarás la solicitud, validarás los datos enviados para asegurarte de que el usuario no haya enviado un formulario vacío, y lo agregarás a la lista messages.

La función de vista create() actualmente solo hace una cosa: renderiza una plantilla llamada create.html cuando recibe una solicitud GET regular. Ahora crearás esta plantilla y luego editarás la función para manejar solicitudes POST en el siguiente paso.

Abre un nuevo archivo de plantilla llamado create.html:

  1. nano templates/create.html

Agrega el siguiente código a él:

flask_app/templates/create.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Add a New Message {% endblock %}</h1>
    <form method="post">
        <label for="title">Title</label>
        <br>
        <input type="text" name="title"
               placeholder="Message title"
               value="{{ request.form['title'] }}"></input>
        <br>

        <label for="content">Message Content</label>
        <br>
        <textarea name="content"
                  placeholder="Message content"
                  rows="15"
                  cols="60"
                  >{{ request.form['content'] }}</textarea>
        <br>
        <button type="submit">Submit</button>
    </form>
{% endblock %}

Guarda y cierra el archivo.

En este código, extiendes la plantilla base.html y reemplazas el bloque content con un encabezado <h1> que sirve como título de la página. En la etiqueta <form>, estableces el atributo method en post para que los datos del formulario se envíen al servidor como una solicitud POST.

En el formulario, tienes un campo de entrada de texto llamado title; este es el nombre que usarás en la aplicación para acceder a los datos del formulario del título. Le das a la etiqueta <input> un valor de {{ request.form['title'] }}. Esto es útil para restaurar los datos que el usuario ingresa para que no se pierdan cuando algo sale mal. Por ejemplo, si el usuario olvida llenar el área de texto content requerida, se envía una solicitud al servidor y se devuelve un mensaje de error como respuesta, pero los datos en el título no se perderán porque se guardarán en el objeto global request, y se pueden acceder a través de request.form['title'].

Después del campo de entrada del título, agregas un área de texto llamada content con el valor {{ request.form['content'] }} por las mismas razones mencionadas anteriormente.

Por último, tienes un botón de enviar al final del formulario.

Ahora, con el servidor de desarrollo en funcionamiento, usa tu navegador para navegar a la ruta /create:

http://127.0.0.1:5000/create

Verás una página “Agregar un Nuevo Mensaje” con un campo de entrada para el título del mensaje, un área de texto para el contenido del mensaje y un botón de enviar.

Este formulario envía una solicitud POST a tu función de vista create(). Sin embargo, aún no hay código para manejar una solicitud POST en la función, por lo que no sucede nada después de completar el formulario y enviarlo. En el siguiente paso, manejarás la solicitud POST entrante cuando se envíe un formulario. Verificarás si los datos enviados son válidos (no están vacíos) y agregarás el título y el contenido del mensaje a la lista messages.

Paso 3 — Manejo de Solicitudes de Formulario

En este paso, manejarás las solicitudes de formulario en el lado de la aplicación. Accederás a los datos del formulario que el usuario envía a través del formulario que creaste en el paso anterior y los agregarás a la lista de mensajes. También utilizarás mensajes flash para informar a los usuarios cuando envíen datos inválidos. El mensaje flash solo se mostrará una vez y desaparecerá en la siguiente solicitud (si navegas a otra página, por ejemplo).

Abre el archivo app.py para editar:

  1. nano app.py

Primero, importarás lo siguiente del framework Flask:

  • El objeto global request para acceder a los datos de la solicitud entrante que se enviarán a través del formulario HTML que construiste en el último paso.
  • La función url_for() para generar URLs.
  • La función flash() para mostrar un mensaje cuando se procesa una solicitud (para informar al usuario que todo salió bien, o para informarles de un problema si los datos enviados no son válidos).
  • La función redirect() para redirigir al cliente a una ubicación diferente.

Agrega estas importaciones en la primera línea del archivo:

flask_app/app.py
from flask import Flask, render_template, request, url_for, flash, redirect

# ...

La función flash() almacena mensajes flash en la sesión del navegador del cliente, lo que requiere establecer una clave secreta. Esta clave secreta se utiliza para proteger las sesiones, lo que permite a Flask recordar información de una solicitud a otra, como pasar de la página de nuevo mensaje a la página de índice. El usuario puede acceder a la información almacenada en la sesión, pero no puede modificarla a menos que tenga la clave secreta, por lo que nunca debes permitir que nadie acceda a tu clave secreta. Consulta la documentación de Flask sobre sesiones para obtener más información.

La clave secreta debe ser una cadena aleatoria larga. Puedes generar una clave secreta utilizando el módulo os con el método os.urandom(), que devuelve una cadena de bytes aleatorios adecuados para uso criptográfico. Para obtener una cadena aleatoria utilizando este método, abre una nueva terminal y abre el shell interactivo de Python con el siguiente comando:

  1. python

En el shell interactivo de Python, importa el módulo os de la biblioteca estándar y llama al método os.urandom() de la siguiente manera:

  1. import os
  2. os.urandom(24).hex()

Obtendrás una cadena similar a la siguiente:

Output
'df0331cefc6c2b9a5d0208a726a5d1c0fd37324feba25506'

Puedes usar la cadena que obtengas como tu clave secreta.

Para establecer la clave secreta, añade una configuración SECRET_KEY a tu aplicación a través del objeto app.config. Añádelo directamente después de la definición de app antes de definir la variable messages:

flask_app/app.py

# ...
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your secret key'


messages = [{'title': 'Message One',
             'content': 'Message One Content'},
            {'title': 'Message Two',
             'content': 'Message Two Content'}
            ]
# ...

A continuación, modifica la función de vista create() para que se vea exactamente como sigue:

flask_app/app.py
# ...

@app.route('/create/', methods=('GET', 'POST'))
def create():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']

        if not title:
            flash('Title is required!')
        elif not content:
            flash('Content is required!')
        else:
            messages.append({'title': title, 'content': content})
            return redirect(url_for('index'))

    return render_template('create.html')

En la sentencia if asegúrate de que el código que sigue se ejecute solo cuando la solicitud sea una solicitud POST mediante la comparación request.method == 'POST'.

Luego extraes el título y el contenido enviados del objeto request.form que te proporciona acceso a los datos del formulario en la solicitud. Si no se proporciona el título, se cumpliría la condición if not title. En ese caso, muestras un mensaje al usuario informándoles que el título es obligatorio utilizando la función flash(). Esto añade el mensaje a una lista de mensajes flash. Más adelante, mostrarás estos mensajes en la página como parte de la plantilla base.html. De manera similar, si no se proporciona el contenido, se cumplirá la condición elif not content. Si es así, añades el mensaje '¡Se requiere contenido!' a la lista de mensajes flash.

Si el título y el contenido del mensaje se envían correctamente, utilizas la línea messages.append({'title': title, 'content': content}) para añadir un nuevo diccionario a la lista messages, con el título y el contenido proporcionados por el usuario. Luego usas la función redirect() para redirigir a los usuarios a la página de índice. Utilizas la función url_for() para enlazar a la página de índice.

Guarda y cierra el archivo.

Ahora, navega a la ruta /create usando tu navegador web:

http://127.0.0.1:5000/create

Rellena el formulario con un título de tu elección y algún contenido. Una vez que envíes el formulario, verás el nuevo mensaje listado en la página de índice.

Por último, mostrarás los mensajes flash y añadirás un enlace a la página “Nuevo Mensaje” en la barra de navegación de la plantilla base.html para tener fácil acceso a esta nueva página. Abre el archivo de la plantilla base:

  1. nano templates/base.html

Edita el archivo agregando una nueva etiqueta `<a>` después del enlace FlaskApp en la barra de navegación dentro de la etiqueta `<nav>`. Luego, agrega un nuevo bucle `for` justo encima del bloque `content` para mostrar los mensajes flash debajo de la barra de navegación. Estos mensajes están disponibles en la función especial `get_flashed_messages()` que proporciona Flask. Luego, agrega un atributo de clase llamado `alert` a cada mensaje y dale algunas propiedades CSS dentro de la etiqueta `<style>`:

flask_app/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %} - FlaskApp</title>
    <style>
        .message {
            padding: 10px;
            margin: 5px;
            background-color: #f3f3f3
        }
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }

        .alert {
            padding: 20px;
            margin: 5px;
            color: #970020;
            background-color: #ffd5de;
        }

    </style>
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">FlaskApp</a>
        <a href="{{ url_for('create') }}">Create</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% for message in get_flashed_messages() %}
            <div class="alert">{{ message }}</div>
        {% endfor %}
        {% block content %} {% endblock %}
    </div>
</body>
</html>

Guarda y cierra el archivo, y luego recarga `https://127.0.0.1:5000` en tu navegador. La barra de navegación ahora tendrá un elemento “Create” que enlaza a la ruta `/create`.

Para ver cómo funcionan los mensajes flash, ve a la página “Create” y haz clic en el botón Submit sin llenar los dos campos. Recibirás un mensaje que se verá así:

Regresa a la página de índice y verás que los mensajes flash debajo de la barra de navegación desaparecen, incluso aunque se muestren como parte de la plantilla base. Si no fueran mensajes flash, también se mostrarían en la página de índice, porque también hereda de la plantilla base.

Intenta enviar el formulario con un título pero sin contenido. Verás el mensaje “¡Se requiere contenido!”. Haz clic en el enlace FlaskApp en la barra de navegación para volver a la página de índice, luego haz clic en el botón Atrás para regresar a la página Crear. Verás que el mensaje de contenido aún está allí. Esto solo funciona si haces clic en el botón Atrás, porque guarda la solicitud anterior. Hacer clic en el enlace Crear en la barra de navegación enviará una nueva solicitud, que borra el formulario, y como resultado, el mensaje flash desaparecerá.

Ahora sabes cómo recibir la entrada del usuario, cómo validarla y cómo agregarla a una fuente de datos.

Nota:
Los mensajes que agregues a la lista messages desaparecerán cada vez que se detenga el servidor, porque las listas de Python solo se guardan en la memoria. Para guardar tus mensajes de manera permanente, necesitarás usar una base de datos como SQLite. Consulta Cómo usar el módulo sqlite3 en Python 3 para aprender a usar SQLite con Python.

Conclusión

Creaste una aplicación Flask donde los usuarios pueden agregar mensajes a una lista de mensajes mostrados en la página de índice. Creaste un formulario web, manejaste los datos que el usuario envía a través del formulario y los agregaste a tu lista de mensajes. También usaste mensajes flash para informar al usuario cuando envía datos no válidos.

Si deseas leer más sobre Flask, consulta los otros tutoriales de la serie Flask.

Source:
https://www.digitalocean.com/community/tutorials/how-to-use-web-forms-in-a-flask-application