Tienes un cupón activo hasta el . Ver tu oferta

¿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 Pocoo. Hoy Flask se mantiene dentro del proyecto Pallets. Flask se basa en el rápido y extensible motor de plantillas Jinja y en el conjunto de herramientas Werkzeug. Además, Flask es una aplicación WSGI (Web Server Gateway Interface), un estándar para la comunicación entre un programa Python que se ejecuta en el servidor y el propio servidor web.

WSGI no es obra de Ronacher. Es un estándar definido en el PEP 3333, cuyo autor es Phillip J. Eby, mientras que Ronacher creó Flask, Werkzeug y Jinja. Según Ronacher, 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.
  • Concurrencia limitada por diseño. Desde Flask 2.0 puedes definir vistas con async def (instalando flask[async]), pero Flask es una aplicación WSGI y cada petición sigue ocupando un worker, incluso en las vistas asíncronas. En producción, Flask se ejecuta con un servidor WSGI como Gunicorn, con varios workers o hilos. Para aplicaciones con mucha concurrencia existen alternativas como Quart (una reimplementación de Flask sobre ASGI) o FastAPI.
  • 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 una buena opción para proyectos pequeños, prototipos e ideas rápidas. En proyectos grandes tendrás que elegir y mantener tú mismo muchas piezas, y si tu aplicación es principalmente asíncrona conviene valorar alternativas como Quart.

Cómo crear un proyecto en Flask

Para comenzar a trabajar con el microframework, debes instalar la última versión de Flask. Lo recomendable es hacerlo dentro de un entorno virtual, para que las dependencias de cada proyecto queden aisladas:

python -m venv .venv
source .venv/bin/activate
pip install flask

En Windows, el entorno se activa con .venv\Scripts\activate.

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, example.com. 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 con el comando flask, activando el modo de depuración:

flask --app hello run --debug

También puedes ejecutar python hello.py, porque el archivo incluye app.run(debug=True), pero el comando flask es la forma que recomienda la documentación oficial.

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/. Se advierte, además, que el servidor local no es adecuado para la producción (allí se usa un servidor WSGI como Gunicorn). 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/Gomez/Ana">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/Gomez/Ana. Esta ruta URL será manejada por la función get_user.

La función get_user está decorada con @app.route('/user/<surname>/<name>'), y en la barra de direcciones tenemos /user/Gomez/Ana. 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, para pasar el encabezado a cada plantilla:

from flask import Flask, render_template

app = Flask(__name__)

@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)

Conectamos Bootstrap

Bootstrap es un conjunto de herramientas de código abierto y gratuitas para crear sitios web y aplicaciones web. Para usarlo con Flask instalaremos la extensión Bootstrap-Flask (pip install bootstrap-flask), que admite Bootstrap 4 y Bootstrap 5. En este ejemplo usamos Bootstrap 5.

En nuestro proyecto, dentro de la carpeta templates tenemos un subdirectorio llamado bootstrap y dentro de él un archivo base.html, que es una plantilla ligeramente modificada a partir de la documentación de Bootstrap-Flask (la extensión no incluye plantillas base propias, así que la creamos nosotros):

<!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="p-5 mb-4 bg-light rounded-3 text-center">
            <h1>{{ h1 }}</h1>
        </div>
    {% endblock %}

    {% block scripts %}
        <!-- JavaScript opcional -->
        {{ bootstrap.load_js() }}
    {% endblock %}
</body>
</html>

En los archivos index.html 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 Bootstrap5
from flask import Flask, render_template

app = Flask(__name__)
bootstrap = Bootstrap5(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="mb-3">
               <label for="eventDate" class="form-label">Fecha</label>
               <input type="date" name="eventDate" class="form-control" placeholder="Fecha del evento">
           </div>
           <div class="mb-3">
               <label for="eventName" class="form-label">Evento</label>
               <input type="text" name="eventName" class="form-control" placeholder="Nombre del evento">
           </div>
           <div class="mb-3">
               <label for="eventDuration" class="form-label">Duración (horas)</label>
               <input type="number" name="eventDuration" class="form-control" placeholder="Duración en horas" 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() }}. Ten en cuenta que este formulario es solo un ejemplo de aprendizaje, y que en una aplicación real conviene proteger los formularios POST contra CSRF, por ejemplo con la extensión Flask-WTF.

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 Bootstrap5
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)
bootstrap = Bootstrap5(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} h"
       )


@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 "Enviar", 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 = db.session.execute(db.select(Event).order_by(Event.date)).scalars().all()
   return render_template("events.html", h1 = "Eventos", events=events)

Fíjate en que la consulta usa db.session.execute(db.select(...)), el estilo que recomienda hoy la documentación de Flask-SQLAlchemy. El estilo anterior, Event.query, se considera heredado (legacy) en SQLAlchemy.

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, y no con flask run, porque la tabla se crea en el bloque __main__ (en algunos casos, es posible que debas escribir el nombre del directorio antes del nombre del archivo app.py).

Más lecturas sobre Flask

  • Documentación de Flask
  • Serie de artículos en Real Python
  • Diseñar una API RESTful con Python y Flask

Para seguir aprendiendo

Leer otros artículos de Blog

Lee otros artículos relevantes del mundo de la tecnología y el espíritu empresarial.

Código

Lenguaje de programación C++

Lenguaje de programación C++: ¿en qué se diferencia de C, dónde se aplica y es adecuado para empezar?

Actualizado el

Código · Tecnologías

El sistema de tipos en TypeScript

Hemos traducido un gran artículo escrito por un antiguo desarrollador de Amazon Web Services, He Zhenghao, y hemos aprendido

Actualizado el

Todos los artículos