Flask: MVC

Aprende a implementar el patrón MVC (Modelo-Vista-Controlador) en Flask, organiza controladores y aprovecha la potencia de Jinja para generar vistas dinámicas. Mejora la mantenibilidad y la claridad de tu proyecto web en Python.

Aprende Flask GRATIS y certifícate

En el desarrollo de aplicaciones web con Flask, la adopción de un patrón MVC (Modelo-Vista-Controlador) brinda una estructura limpia y ordenada, facilitando la separación de responsabilidades. Con el microframework Flask, y la integración de Jinja como motor de plantillas, se obtienen vistas dinámicas y flexibles para la capa de presentación.

Qué es el patrón MVC

El patrón MVC consiste en dividir la aplicación en tres componentes principales:

  1. Modelo (Model): Gestiona la lógica de la aplicación relacionada con los datos, usualmente enlazado con la base de datos.
  2. Vista (View): Se encarga de la representación visual de la información, habitualmente mediante plantillas.
  3. Controlador (Controller): Actúa como intermediario, recibiendo las peticiones del usuario y orquestando la interacción entre el modelo y la vista.

En Flask, los modelos suelen desarrollarse con librerías como SQLAlchemy, las vistas se construyen con Jinja y los controladores se establecen a través de funciones que manejan las rutas.

Organización en Flask

Una estructura recomendable para proyectos de tamaño medio o grande en Flask podría ser la siguiente:

mi_proyecto/
├── app.py
├── config.py
├── models/
│   └── usuario.py
├── controllers/
│   └── usuario_controller.py
├── templates/
│   └── usuario/
│       └── detalle.html
│       └── lista.html
├── static/
│   └── css/
│   └── js/
└── requirements.txt
  • app.py: Contiene la aplicación Flask y la configuración inicial.
  • models/: Aloja las entidades que representan las tablas de la base de datos.
  • controllers/: Contiene los archivos que gestionan la lógica de cada recurso o entidad.
  • templates/: Carpeta para las vistas de Jinja, separadas por secciones.
  • static/: Para archivos estáticos como CSS, JavaScript o imágenes.

Definición de modelos

En la carpeta models, se puede ubicar una clase que represente, por ejemplo, un usuario en la base de datos:

# models/usuario.py
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Usuario(db.Model):
    __tablename__ = 'usuarios'

    id = db.Column(db.Integer, primary_key=True)
    nombre = db.Column(db.String(50), nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

    def __repr__(self):
        return f'<Usuario {self.nombre}>'

Este archivo define el modelo que interactúa con la base de datos, aportando la columna id como clave primaria, así como nombre y email para los datos del usuario.

Controladores y rutas

En Flask, cada ruta se asocia a una función (u objeto) que orquesta la lógica principal. En el patrón MVC, esas funciones representan el controlador. Un ejemplo de controlador para usuarios:

# controllers/usuario_controller.py
from flask import Blueprint, render_template, request, redirect, url_for
from models.usuario import Usuario, db

usuario_bp = Blueprint('usuario_bp', __name__)

@usuario_bp.route('/usuarios', methods=['GET'])
def lista_usuarios():
    usuarios = Usuario.query.all()
    return render_template('usuario/lista.html', usuarios=usuarios)

@usuario_bp.route('/usuarios/<int:usuario_id>', methods=['GET'])
def detalle_usuario(usuario_id):
    usuario = Usuario.query.get_or_404(usuario_id)
    return render_template('usuario/detalle.html', usuario=usuario)

@usuario_bp.route('/usuarios/crear', methods=['POST'])
def crear_usuario():
    nombre = request.form.get('nombre')
    email = request.form.get('email')
    nuevo_usuario = Usuario(nombre=nombre, email=email)
    db.session.add(nuevo_usuario)
    db.session.commit()
    return redirect(url_for('usuario_bp.lista_usuarios'))

Aquí se ilustran tres rutas:

  1. lista_usuarios: Devuelve la lista completa de usuarios consultando el modelo.
  2. detalle_usuario: Muestra la información de un usuario en particular.
  3. crear_usuario: Procesa un formulario enviado por método POST, crea un registro en la base de datos y redirige a la lista.

Para usar este controlador, se registra en app.py (o en un archivo principal), de la siguiente manera:

from flask import Flask
from controllers.usuario_controller import usuario_bp
from models.usuario import db

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///mi_base.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db.init_app(app)
app.register_blueprint(usuario_bp)

with app.app_context():
    db.create_all()

if __name__ == '__main__':
    app.run(debug=True)

Uso de plantillas Jinja

La capa de vista en el patrón MVC de Flask se maneja mediante Jinja. Este motor de plantillas lee archivos HTML y permite incluir variables y estructuras de control. Un ejemplo en templates/usuario/lista.html:

<!DOCTYPE html>
<html>
<head>
    <title>Lista de usuarios</title>
</head>
<body>
    <h1>Lista de usuarios</h1>
    <ul>
        {% for usuario in usuarios %}
            <li>
                <a href="{{ url_for('usuario_bp.detalle_usuario', usuario_id=usuario.id) }}">
                    {{ usuario.nombre }} ({{ usuario.email }})
                </a>
            </li>
        {% endfor %}
    </ul>
</body>
</html>

La variable usuarios, pasada desde el controlador, se itera con la sintaxis {% for ... in ... %} y se muestra el nombre y el correo.

Otra plantilla para templates/usuario/detalle.html podría:

<!DOCTYPE html>
<html>
<head>
    <title>Detalle del usuario</title>
</head>
<body>
    <h1>Detalle de {{ usuario.nombre }}</h1>
    <p>Correo: {{ usuario.email }}</p>
    <a href="{{ url_for('usuario_bp.lista_usuarios') }}">Volver a la lista</a>
</body>
</html>

Mediante Jinja, se mezcla lógica mínima de presentación con variables y enlaces que devuelven información dinámica al usuario.

Formularios y envío de datos

Para crear un nuevo usuario, se puede ofrecer un formulario en una plantilla:

<form action="{{ url_for('usuario_bp.crear_usuario') }}" method="POST">
    <label for="nombre">Nombre</label>
    <input type="text" name="nombre" id="nombre" required>
    <label for="email">Email</label>
    <input type="email" name="email" id="email" required>
    <button type="submit">Crear</button>
</form>

Al enviarse, la ruta usuario_bp.crear_usuario gestiona los datos y los inserta en la base de datos. Esta arquitectura en Flask separa claramente la vista (HTML) del controlador (lógica de inserción).

Buenas prácticas en el patrón MVC

  • Separar la lógica de negocio (modelos) de la lógica de control (rutas).
  • Ubicar archivos HTML en templates/, agrupados por sección para mantener orden.
  • Emplear Blueprints para dividir el proyecto en múltiples controladores, cada uno enfocado en un recurso o módulo distinto.
  • Reutilizar partes de la vista con la herencia de plantillas de Jinja, a través de archivos base y bloques que se extienden en subplantillas.
  • Definir funciones claras y pequeñas en los controladores, facilitando la lectura y la testabilidad del código.

Extensión con un sistema de archivos estáticos

El directorio static/ se utiliza para hospedar archivos CSS, JavaScript o imágenes. En Flask, se accede a estos recursos a través de la ruta /static. Por ejemplo, si se ubica un archivo styles.css dentro de static/css/, se puede enlazar en una plantilla:

<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">

Esta organización ofrece coherencia entre los módulos, los assets y las plantillas del proyecto.

Al llevar el patrón MVC a tu aplicación de Flask, se favorece la escalabilidad, la legibilidad y la coherencia de todo el proyecto. Separar el código en modelos, controladores y vistas no solo mejora el mantenimiento, sino que también agiliza el crecimiento y la incorporación de nuevas funcionalidades en la aplicación. Con la potencia de Jinja como motor de plantillas, la creación de interfaces dinámicas se adapta con naturalidad a las necesidades de cada proyecto web en Python.

Empezar curso de Flask

Lecciones de este módulo de Flask

Lecciones de programación del módulo MVC del curso de Flask.