¿Qué es Flask y cómo crear rápidamente sitio web usándolo?

Cómo escribir un sitio web en Flask
Hablaremos sobre uno de los microframeworks más populares y concisos para Python: Flask. Cómo crear una aplicación sencilla, conectarla con Bootstrap y una base de datos, y si Flask tiene alguna desventaja en absoluto.
Flask es un microframework para crear aplicaciones web en Python. Viene con un conjunto mínimo de herramientas, pero admite extensiones como si estuvieran implementadas en Flask mismo. Las extensiones para el microframework permiten comunicarse con bases de datos, validar formularios, controlar la carga en el servidor, trabajar con autenticación y mucho más.

La primera versión pública de Flask se lanzó el 16 de abril de 2010. El autor del proyecto es Armin Ronacher, quien lideró el equipo de entusiastas de Python en Poocco. Flask se basa en el rápido y extensible motor de plantillas Jinja y en el conjunto de herramientas Werkzeug. Además, Flask utiliza una de las bibliotecas de servicios más avanzadas de WSGI (Web Server Gateway Interface), que es un estándar para la comunicación entre un programa Python que se ejecuta en el servidor y el propio servidor web.

WSGI también fue desarrollado por Armin Ronacher. Según él, la idea de Flask fue originalmente una broma del Día de los Inocentes que se hizo popular y se convirtió en una aplicación seria.
Aprende Python en Códica:

Realiza nuestra profesión de "Desarrollador de Python" para cambiar tu vida y convertirte en un programador de backend.
Ventajas y desventajas de Flask
Casi todas las ventajas y desventajas de Flask surgen del hecho de que es un microframework.

Entre las ventajas:
  • Simplicidad. Flask es fácil de instalar y configurar.
  • Flexibilidad. El microframework permite a los desarrolladores elegir las tecnologías y herramientas que desean utilizar en sus proyectos.
  • Extensibilidad. Flask permite ampliar la funcionalidad mediante complementos y módulos que se pueden integrar fácilmente en el proyecto.
  • Comunidad activa. Flask es uno de los frameworks más utilizados para Python, por lo que cuenta con una gran comunidad de desarrolladores.
Sin embargo, Flask también tiene sus desventajas:
  • Falta de soluciones listas para usar. Inicialmente, Flask solo proporciona un conjunto mínimo de funcionalidades. Si un programador necesita funcionalidades más amplias, como autenticación de usuarios, tendrá que agregar bibliotecas adicionales o implementarlas por sí mismo.
  • Falta de soporte nativo para subprocesos múltiples. Flask fue diseñado como un framework de un solo subproceso. Para manejar aplicaciones web de subprocesos múltiples, se deben instalar bibliotecas adicionales.
  • Limitaciones en la capacidad de escalar. Si un proyecto comienza a crecer y volverse más complejo, puede haber dificultades para mantener la funcionalidad necesaria.
En resumen, Flask es ideal para proyectos pequeños, es perfecto para prototipos e ideas rápidas. Sin embargo, rara vez se utiliza en proyectos grandes y no es adecuado para la programación asíncrona.
Cómo crear un proyecto en Flask
Para comenzar a trabajar con el microframework, debes descargar la última versión de Flask:

pip install Flask

A modo de ejemplo, escribiremos una aplicación web de prueba con funcionalidad mínima en Flask. Así es como funcionan las aplicaciones de este tipo:
  • El usuario ingresa una URL en el navegador, por ejemplo, hexlet.io. En nuestra aplicación de prueba, el usuario no ingresará una URL, ya que estaremos trabajando con un servidor local en la dirección http://127.0.0.1:5000.
  • El navegador obtiene la dirección IP del servidor DNS necesario para nosotros. DNS es el Sistema de Nombres de Dominio, una red distribuida de servidores que funciona como una "guía telefónica" en Internet.
  • El navegador envía una solicitud a esa dirección y recibe una respuesta, generalmente en forma de una página HTML.
  • El navegador muestra el contenido de la página.
Entonces, crearemos un archivo hello.py y escribiremos el siguiente código en él:
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index() -> str:
    return '<p>Hello Flask!</p>'

if __name__ == '__main__':
    app.run(debug=True)
Veamos en detalle qué hace el código que hemos escrito.

La primera línea importa la clase Flask. Luego creamos una instancia de esta clase, pasando el nombre del módulo como primer argumento. Este será nuestro objeto de aplicación para comunicarnos con el servidor web. __name__ es una forma conveniente de pasar la aplicación desde la que se ejecuta Flask.

El decorador route() le comunica a Flask qué función del desarrollador se ejecutará cuando se acceda a una determinada URL, en nuestro caso, index. La última línea inicia un servidor web local con el parámetro debug=True, lo que nos permite ver todos los errores en el registro del programa.
Lee también:

Programación en Python: características de aprendizaje, perspectivas, situación en el mercado laboral
Ejecutamos la aplicación web desde la terminal:

python hello.py

Si todo se hizo correctamente, veremos estos mensajes en la terminal:
Principalmente, se muestra información de servicio aquí. Lo único que nos interesa es el mensaje de que nuestro servidor local se está ejecutando en la dirección http://127.0.0.1:5000/. En rojo, se indica que el servidor local no es adecuado para la producción. Sin embargo, como hemos implementado una aplicación de prueba, no la desplegaremos en un servidor real.

Volviendo al código. Con la parte variable de la ruta, Flask puede pasar argumentos a la función.
from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    greet_with_link = """<h1>Hola, mundo!</h1>
    <p><a href="user/Anikin/Georgy">Presióname</a></p>"""
    return greet_with_link

@app.route('/user/<surname>/<name>')
def get_user(name, surname):
    personal_instruc = f"""<h1>Hola, {surname} {name}!</h1>
    <p>Cambia el nombre de usuario en la barra de dirección y vuelve a cargar la página</p>"""
    return personal_instruc

if __name__ == '__main__':
    app.run(debug=True)
En nuestro ejemplo, los valores simplemente aparecerán en el navegador como parte de la cadena. En la página de inicio de nuestro sitio web, la función index() se ejecutará. Además del saludo, se le pedirá al usuario que haga clic en un enlace que lo llevará a user/Anikin/Georgy. Esta ruta URL será manejada por la función get_user.

La función get_user está decorada con @app.route('/<surname>/<name>'), y en la barra de direcciones tenemos /user/Anikin/Georgy. Es decir, nuestra función recibe argumentos de la URL, que se encuentran entre barras diagonales. Por defecto, el tipo de estos valores es string, que acepta cualquier texto sin barras diagonales. Sin embargo, las variables de ruta también pueden ser de otros tipos, como int, float, path y otros. Los tipos se especifican en el formato <tipo:nombre de la variable>.
Estructura de una aplicación Flask
Crearemos un subdirectorio flask_app con la siguiente estructura de archivos y carpetas:
Para escribir una aplicación que sea más que una sola línea, el directorio del proyecto debe contener las carpetas static y templates. El directorio static contiene recursos utilizados por las plantillas, incluyendo archivos CSS, JavaScript e imágenes. La carpeta templates solo contiene plantillas con la extensión *.html.

Llenemos nuestros archivos con código. Primero, el archivo principal de nuestro proyecto app.py:
from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html")


@app.route("/about")
def get_page_about():
    return render_template("about.html")

if __name__ == "__main__":
    app.run(debug=True)
Luego, index.html:
<!DOCTYPE html>
<html>
    <head>
        <title>Main page</title>
    </head>
    <body>
        <h1>Página principal</h1>
    </body>
</html>
Y el archivo about.html:
<!DOCTYPE html>
<html>
    <head>
        <title>About</title>
    </head>
    <body>
        <h1>Acerca de la app</h1>
    </body>
</html>
Para mostrar las plantillas HTML, utilizamos la función render_template(). En nuestro código, solo toma el nombre de la plantilla y devuelve una cadena con el resultado de la renderización de la plantilla.

Sin embargo, render_template() puede tomar un número ilimitado de argumentos con nombre, que se pueden utilizar en la plantilla. Esto nos permite resolver el problema de nuestro proyecto de prueba: actualmente tenemos dos funciones, dos páginas y mucho código duplicado.

!youtube!(WQV3eDIAyk8)

Escribiremos una plantilla base base.html y un par de plantillas hijas. Los bloques {% block smth %} … {% endblock %} son partes de la plantilla base que se pueden reemplazar en la plantilla hija. Las variables se pasan por nombre en la construcción {{ variable }}.
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}Aplicación Flask{% endblock %}</title>
</head>
<body>
    <h1>{{h1}}</h1>
</body>
</html>
Después de crear el archivo con la plantilla base de HTML, podemos corregir nuestras otras plantillas HTML:
about.html:
{% extends 'base.html' %}
{% block title %}About{% endblock %}

index.html:
{% extends 'base.html' %}
{% block title %}Main page{% endblock %}
Además, debemos corregir nuestro archivo principal del proyecto Flask app.py:
about.html:
{% extends 'base.html' %}
{% block title %}About{% endblock %}

index.html:
{% extends 'base.html' %}
{% block title %}Main page{% endblock %}
##Conectamos Bootstrap

Bootstrap es un conjunto de herramientas de código abierto y gratuitas para crear sitios web y aplicaciones web.

En nuestro proyecto, dentro de la carpeta templates tenemos un subdirectorio llamado bootstrap y dentro de él un archivo base.htm, que es una plantilla ligeramente modificada para la documentación del sitio Bootstrap-Flask:
<!doctype html>
<html lang="es">
    <head>
    {% block head %}
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    {% block styles %}
        <!-- Bootstrap CSS -->
        {{ bootstrap.load_css() }}
    {% endblock %}

    <title>{% block title %}Aplicación Flask{% endblock %}</title>
    {% endblock %}
</head>
<body>
    <!-- Contenido de tu página -->
    {% block content %}
        <div class="jumbotron text-center">
            <h1>{{ h1 }}</h1>
        </div>
    {% endblock %}

    {% block scripts %}
        <!-- JavaScript opcional -->
        {{ bootstrap.load_js() }}
    {% endblock %}
</body>
</html>
En los archivos index.htm y `about.html, reemplazaremos la línea de herencia por:
{% extends 'bootstrap/base.html' %}
Otra forma de conectar Bootstrap a un proyecto Flask es a través de CDN. Puedes obtener más información al respecto en la documentación del framework.

Después de conectar Bootstrap, tendrás que hacer algunos ajustes en el archivo principal de nuestro proyecto app.py:

from flask_bootstrap import Bootstrap4
from flask import Flask, render_template

app = Flask(__name__)
bootstrap = Bootstrap4(app)

@app.route("/")
def index():
    return render_template("index.html", h1 = "Página principal")

@app.route("/about")
def get_page_about():
    return render_template("about.html", h1 = "Acerca de la app")

if __name__ == "__main__":
    app.run(debug=True)
Por último, pero no menos importante, agregaremos un formulario de envío. Para ello, modificaremos ligeramente index.html:
{% extends 'bootstrap/base.html' %}
{% block title %}Main page{% endblock %}
{% block content %}
   {{super()}}
   <div class="container text-center">
       <form class="d-inline-block" style="max-width: 33%;">
           <div class="form-group">
               <label for="eventDate">Fecha</label>
               <input type="date" name="eventDate" class="form-control" placeholder="Fecha события">
           </div>
           <div class="form-group">
               <label for="eventName">Evento</label>
               <input type="text" name="eventName" class="form-control" placeholder="Nombre события">
           </div>
           <div class="form-group">
               <label for="eventDuration">Duración</label>
               <input type="number" name="eventDuration" class="form-control" placeholder="Продолжительность" min="1" max="24">
           </div>
           <button type="submit" class="btn btn-primary">Enviar</button>
       </form>
   </div>
{% endblock %}
En general, Bootstrap puede agregar una gran cantidad de elementos a la aplicación con solo unos pocos clics. Nos hemos limitado a cuatro: tres campos y un botón. El elemento clave aquí es {{ super() }}.
Conexión con una base de datos
Ahora tenemos un formulario de envío, pero actualmente no hace nada con los datos. Sería conveniente almacenar, procesar y recuperar fácilmente los datos de estos formularios en el futuro. Por lo general, estas tareas se resuelven utilizando bases de datos relacionales (DB).

Hay muchas formas de trabajar con consultas SQL en Flask. Podemos usar, por ejemplo, sqlite3 y SQL puro, o podemos usar la biblioteca sqlite3 para Python. Además, se pueden envolver las consultas SQL puras en código o utilizar Psycopg2 para trabajar con PostgresSQL en Python. Para este ejemplo, usaremos la biblioteca Flask SQLAlchemy (una extensión para Flask) que ofrece tecnología ORM para interactuar con la DB.

Conectemos la base de datos a nuestro proyecto a través del archivo app.py:
from datetime import datetime


from flask import Flask, redirect, render_template, request
from flask_bootstrap import Bootstrap4
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)
bootstrap = Bootstrap4(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///events.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)


class Event(db.Model):
   id = db.Column(db.Integer, primary_key=True)
   date = db.Column(db.Date, nullable=False)
   name = db.Column(db.String(255), nullable=False)
   duration = db.Column(db.Integer, nullable=False)
  
   def __str__(self):
       return (
           f"Nombre: {self.name}\n"
           f"Fecha: {self.date}\n"
           f"Duración {self.duration}ч"
       )


@app.route('/', methods=['POST'])
def add_event():
   date = datetime.strptime(request.form['eventDate'], '%Y-%m-%d').date()
   name = request.form['eventName']
   duration = int(request.form['eventDuration'])
   print(date, name, duration, sep='\n')
   event = Event(date=date, name=name, duration=duration)
   db.session.add(event)
   db.session.commit()
   return redirect('/')


@app.route("/")
def index():
   return render_template("index.html", h1 = "Página principal")


@app.route("/about")
def get_page_about():
   return render_template("about.html", h1 = "Acerca de la app")


if __name__ == "__main__":
   with app.app_context():
       db.create_all()
   app.run(debug=True)
En nuestra DB, hemos creado la clase Event con atributos que hereda de db.Model. Esto nos permite crear una tabla event con columnas que son los campos de nuestra clase. Además, hemos definido el método mágico __str__ para la representación de cadena de los objetos de la clase, lo cual será útil para mostrarlos en HTML.

Para crear la tabla, hemos agregado el comando db.create_all() al bloque if __name__ == '__main__', y para manejar el formulario enviado, hemos agregado el método add_event. Este método funciona con el método POST, que le indica a Flask que los datos se enviarán al servidor.

En el método POST, leemos los datos del formulario enviado y creamos variables temporales para cada campo. Luego, creamos un objeto event de la clase Event, pasando nuestras variables temporales como argumentos con nombre, lo agregamos a la DB y confirmamos los cambios.

Solo nos queda ajustar un poco el formulario: en el archivo index.html, agregamos los atributos action="{{ url_for('add_event') }}" method="POST" a la etiqueta de apertura <form>. Ahora, cuando se hace clic en el botón "Guardar", el formulario enviará los datos a la base de datos.

Agreguemos una página para mostrar nuestros registros en un nuevo archivo Events.html:
{% extends 'bootstrap/base.html' %}
{% block title %}Events{% endblock %}
{% block content %}
   {{super()}}
   <div class="container text-center">
       <a href="{{ url_for('index') }}"><h2>Agregar evento</h2></a>
   </div>
   <div class="mt-4">
       <ul class="list-group">
           {% for event in events %}
               <li class="list-group-item">
                   <p>{{ event }}</p>
               </li>
           {% endfor %}
       </ul>
   </div>
{% endblock %}
En el archivo app.py, agregamos la vista:
@app.route("/events")
def view_events():
   events = Event.query.order_by(Event.date).all()
   return render_template("events.html", h1 = "Eventos", events=events)
Y en el contenedor principal de index.html, agregamos un enlace a esta página:
<a href="{{ url_for('view_events') }}"><h2>Ver eventos</h2></a>
¡Nuestro proyecto de prueba en Flask está listo! Puedes ejecutarlo en un servidor local con el comando python app.py (en algunos casos, es posible que debas escribir el nombre del directorio antes del nombre del archivo app.py).
Más lecturas sobre Flask
Leer otros artículos de Blog
Lee otros artículos relevantes del mundo de la tecnología y el espíritu empresarial.