0%
Flask: Crea aplicaciones web ligeras en Python

Flask: Crea aplicaciones web ligeras en Python

Descubre Flask para crear aplicaciones web ligeras en Python: rutas, plantillas Jinja2, formularios, API JSON, blueprints y despliegue en producción.

I

InSkillCoach

· min

Flask: Crea aplicaciones web ligeras en Python

Flask es un microframework web para Python: ofrece lo estrictamente necesario para construir una aplicación web (enrutamiento, plantillas, gestión de peticiones) y te deja elegir el resto (base de datos, autenticación, formularios). Esta filosofía minimalista lo convierte en un excelente punto de partida para aprender desarrollo web en Python, y es lo bastante potente para aplicaciones en producción. Esta guía te acompaña desde la instalación hasta el despliegue.

Instalación y aplicación mínima

Crea un entorno virtual y luego instala Flask:

# Création et activation de l'environnement virtuel
python -m venv venv

# Windows
venv\Scripts\activate
# macOS / Linux
source venv/bin/activate

# Installation
pip install flask

Una aplicación Flask completa cabe en unas pocas líneas. Crea un archivo app.py:

from flask import Flask

# Création de l'application
app = Flask(__name__)

# Association d'une URL à une fonction (une "vue")
@app.route('/')
def accueil():
    return "Bonjour, Flask !"

if __name__ == '__main__':
    # debug=True : rechargement automatique et messages d'erreur détaillés
    app.run(debug=True)

Lanza el servidor de desarrollo:

python app.py
# * Running on http://127.0.0.1:5000

Abre http://127.0.0.1:5000 en tu navegador: la página muestra «Bonjour, Flask !». No uses nunca debug=True en producción: el depurador integrado permite ejecutar código arbitrario.

Rutas y métodos HTTP

El decorador @app.route asocia una URL a una función. Por defecto, una ruta solo responde a peticiones GET; el parámetro methods permite otros verbos HTTP:

from flask import Flask, request

app = Flask(__name__)

@app.route('/contact', methods=['GET', 'POST'])
def contact():
    if request.method == 'POST':
        # Traitement de l'envoi du formulaire
        return "Message reçu"
    # Affichage du formulaire (GET)
    return "Formulaire de contact"
MétodoUso típico
GETLeer un recurso, mostrar una página
POSTCrear un recurso, enviar un formulario
PUTReemplazar un recurso existente
PATCHModificar parcialmente un recurso
DELETEEliminar un recurso

Parámetros de URL

Los segmentos variables de una URL se declaran entre corchetes angulares y se pasan como argumento a la función:

# Paramètre chaîne (par défaut)
@app.route('/utilisateur/<nom>')
def profil(nom):
    return f"Profil de {nom}"

# Paramètre entier : /article/42 fonctionne, /article/abc renvoie 404
@app.route('/article/<int:article_id>')
def article(article_id):
    return f"Article numéro {article_id}"

# Paramètre chemin : accepte les barres obliques
@app.route('/fichiers/<path:chemin>')
def fichier(chemin):
    return f"Chemin demandé : {chemin}"

Conversores disponibles: string (por defecto), int, float, path, uuid.

Los parámetros de consulta (?tri=date&page=2) se leen con request.args:

@app.route('/recherche')
def recherche():
    terme = request.args.get('q', '')            # valeur par défaut : ''
    page = request.args.get('page', 1, type=int)  # conversion automatique
    return f"Recherche de « {terme} », page {page}"

Plantillas Jinja2

Devolver HTML dentro de cadenas de Python se vuelve ilegible enseguida. Flask integra el motor de plantillas Jinja2: los archivos HTML viven en una carpeta templates/.

Estructura del proyecto:

mon_projet/
├── app.py
├── templates/
│   ├── base.html
│   └── index.html
└── static/
    └── style.css

Herencia de plantillas

La plantilla base define la estructura común; las páginas rellenan los bloques:

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>{% block titre %}Mon Site{% endblock %}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <nav>
        <a href="{{ url_for('accueil') }}">Accueil</a>
    </nav>
    <main>
        {% block contenu %}{% endblock %}
    </main>
</body>
</html>
<!-- templates/index.html -->
{% extends "base.html" %}

{% block titre %}Accueil - Mon Site{% endblock %}

{% block contenu %}
<h1>Bienvenue, {{ nom }} !</h1>

{% if articles %}
<ul>
    {% for article in articles %}
    <li>{{ article.titre }} ({{ article.date }})</li>
    {% endfor %}
</ul>
{% else %}
<p>Aucun article pour le moment.</p>
{% endif %}
{% endblock %}

Del lado de Python, render_template carga la plantilla y le transmite variables:

from flask import render_template

@app.route('/')
def accueil():
    articles = [
        {"titre": "Premier article", "date": "2026-09-01"},
        {"titre": "Deuxième article", "date": "2026-09-10"},
    ]
    return render_template('index.html', nom="Alice", articles=articles)

Jinja2 escapa automáticamente el HTML de las variables, lo que protege frente a ataques XSS.

Archivos estáticos

Las hojas de estilo, los scripts y las imágenes van en la carpeta static/. La función url_for genera sus URL:

<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo">

Usa siempre url_for en lugar de rutas escritas a mano: las URL siguen siendo correctas aunque la aplicación se despliegue bajo un prefijo.

Formularios y objeto request

Los datos de un formulario enviado por POST se leen con request.form:

from flask import Flask, request, render_template

app = Flask(__name__)

@app.route('/inscription', methods=['GET', 'POST'])
def inscription():
    if request.method == 'POST':
        nom = request.form.get('nom', '').strip()
        email = request.form.get('email', '').strip()

        # Validation minimale
        if not nom or not email:
            return render_template('inscription.html',
                                   erreur="Tous les champs sont obligatoires")

        # Ici : enregistrement en base de données
        return f"Bienvenue, {nom} !"

    return render_template('inscription.html')
<!-- templates/inscription.html -->
{% extends "base.html" %}
{% block contenu %}
{% if erreur %}<p class="erreur">{{ erreur }}</p>{% endif %}
<form method="post">
    <input type="text" name="nom" placeholder="Votre nom">
    <input type="email" name="email" placeholder="Votre email">
    <button type="submit">S'inscrire</button>
</form>
{% endblock %}

En proyectos reales, la biblioteca Flask-WTF añade validación y protección CSRF.

Redirecciones y mensajes flash

Tras un POST correcto, redirige al usuario (patrón «Post/Redirect/Get») y muestra un mensaje temporal con flash:

from flask import Flask, flash, redirect, url_for, render_template

app = Flask(__name__)
app.secret_key = "changez-moi-en-production"  # requis pour flash et les sessions

@app.route('/enregistrer', methods=['POST'])
def enregistrer():
    # ... traitement ...
    flash("Enregistrement réussi !", "succes")
    return redirect(url_for('accueil'))
<!-- Dans base.html, avant le bloc contenu -->
{% with messages = get_flashed_messages(with_categories=true) %}
    {% for categorie, message in messages %}
    <div class="flash flash-{{ categorie }}">{{ message }}</div>
    {% endfor %}
{% endwith %}

API JSON con jsonify

Flask es muy adecuado para las API REST. La función jsonify serializa un diccionario como respuesta JSON con la cabecera Content-Type correcta:

from flask import Flask, jsonify, request

app = Flask(__name__)

TACHES = [
    {"id": 1, "titre": "Apprendre Flask", "faite": True},
    {"id": 2, "titre": "Créer une API", "faite": False},
]

@app.route('/api/taches', methods=['GET'])
def lister_taches():
    return jsonify(TACHES)

@app.route('/api/taches/<int:tache_id>', methods=['GET'])
def detail_tache(tache_id):
    tache = next((t for t in TACHES if t["id"] == tache_id), None)
    if tache is None:
        return jsonify({"erreur": "Tâche introuvable"}), 404
    return jsonify(tache)

@app.route('/api/taches', methods=['POST'])
def creer_tache():
    donnees = request.get_json()  # corps JSON de la requête
    if not donnees or "titre" not in donnees:
        return jsonify({"erreur": "Le champ 'titre' est requis"}), 400
    nouvelle = {"id": len(TACHES) + 1, "titre": donnees["titre"], "faite": False}
    TACHES.append(nouvelle)
    return jsonify(nouvelle), 201

Prueba rápida con curl:

curl http://127.0.0.1:5000/api/taches
# [{"faite": true, "id": 1, "titre": "Apprendre Flask"}, ...]

curl -X POST http://127.0.0.1:5000/api/taches \
     -H "Content-Type: application/json" \
     -d '{"titre": "Déployer"}'
# {"faite": false, "id": 3, "titre": "Déployer"}

Estructurar con blueprints

Cuando la aplicación crece, un único archivo se vuelve inmanejable. Los blueprints agrupan las rutas por dominio funcional:

# blog/routes.py
from flask import Blueprint, render_template

blog_bp = Blueprint('blog', __name__, url_prefix='/blog')

@blog_bp.route('/')
def liste_articles():
    return render_template('blog/liste.html')

@blog_bp.route('/<int:article_id>')
def detail(article_id):
    return render_template('blog/detail.html', article_id=article_id)
# app.py
from flask import Flask
from blog.routes import blog_bp

app = Flask(__name__)
app.register_blueprint(blog_bp)
# Les routes /blog/ et /blog/<id> sont maintenant actives

Estructura típica de un proyecto organizado:

mon_projet/
├── app.py
├── blog/
│   └── routes.py
├── auth/
│   └── routes.py
├── templates/
└── static/

Configuración y variables de entorno

No pongas nunca secretos (claves, contraseñas de base de datos) en el código. Cárgalos desde el entorno:

import os
from flask import Flask

app = Flask(__name__)

app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-uniquement')
app.config['DATABASE_URL'] = os.environ.get('DATABASE_URL')
app.config['DEBUG'] = os.environ.get('FLASK_DEBUG', '0') == '1'

En desarrollo, el paquete python-dotenv carga automáticamente un archivo .env (que debes excluir del repositorio Git mediante .gitignore):

# .env
SECRET_KEY=une-longue-chaine-aleatoire
DATABASE_URL=postgresql://user:pass@localhost/mabase
FLASK_DEBUG=1

Despliegue: gunicorn y nginx

El servidor integrado de Flask es monoproceso e inseguro: está reservado para el desarrollo. En producción, usa un servidor WSGI como Gunicorn detrás de un servidor web nginx:

pip install gunicorn

# 4 processus de travail, à l'écoute sur le port 8000
gunicorn --workers 4 --bind 127.0.0.1:8000 app:app

nginx actúa como proxy inverso: recibe el tráfico HTTPS, sirve directamente los archivos estáticos y transmite el resto a Gunicorn:

server {
    listen 80;
    server_name exemple.com;

    location /static/ {
        alias /var/www/mon_projet/static/;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Plataformas como Render, Railway o Fly.io automatizan este montaje si no quieres gestionar un servidor.

Flask vs Django

CriterioFlaskDjango
FilosofíaMicroframework, minimalistaFramework completo, «con baterías incluidas»
Curva de aprendizajeSuave, se aprende según surgen las necesidadesMás pronunciada, muchos conceptos desde el principio
ORMA elegir (SQLAlchemy es lo más habitual)ORM de Django integrado
AdministraciónSe construye o mediante extensiónInterfaz de administración generada automáticamente
AutenticaciónExtensión (Flask-Login)Integrada
Estructura del proyectoLibreImpuesta y convencional
Ideal paraAPI, microservicios, sitios pequeños, prototiposAplicaciones completas con back-office

Ambos frameworks son maduros y se utilizan en producción a gran escala. Elige Flask por su ligereza y flexibilidad, y Django cuando quieras el máximo de funcionalidades listas para usar.

Buenas prácticas

  • Usa siempre un entorno virtual y un archivo requirements.txt (pip freeze > requirements.txt).
  • Desactiva el modo debug en producción: es una vulnerabilidad de seguridad grave.
  • Genera las URL con url_for en lugar de escribirlas a mano.
  • Guarda los secretos en variables de entorno, nunca en el código fuente.
  • Estructura con blueprints en cuanto la aplicación supere unas pocas rutas.
  • Devuelve los códigos HTTP adecuados en tus API: 201 para una creación, 400 para una petición no válida, 404 para un recurso inexistente.
  • Valida todas las entradas del usuario en el servidor, aunque el formulario ya las valide en el cliente.

Conclusión

Flask ilustra a la perfección la filosofía de Python: sencillo para empezar, extensible para crecer. Ahora sabes crear rutas, generar páginas con Jinja2, procesar formularios, exponer una API JSON, organizar tu código con blueprints y desplegar con Gunicorn y nginx. El siguiente paso lógico: conectar una base de datos con Flask-SQLAlchemy, gestionar la autenticación con Flask-Login y proteger tus formularios con Flask-WTF.

Para profundizar, consulta la documentación oficial de Flask, notablemente clara y completa.

InSkillCoach

Acerca de InSkillCoach

Experto en formación y tecnologías

Coach especializado en tecnologías avanzadas e IA, impulsado por GNeurone Inc.

Certificaciones:

  • AWS Certified Solutions Architect – Professional
  • Certificaciones de Google Cloud
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
1.8k
130

Comentarios

Los comentarios funcionan con GitHub Discussions

Inicia sesión con GitHub para participar en la discusión

¡Enlace copiado!