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ícateEn 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:
- Modelo (Model): Gestiona la lógica de la aplicación relacionada con los datos, usualmente enlazado con la base de datos.
- Vista (View): Se encarga de la representación visual de la información, habitualmente mediante plantillas.
- 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:
- lista_usuarios: Devuelve la lista completa de usuarios consultando el modelo.
- detalle_usuario: Muestra la información de un usuario en particular.
- 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.
Lecciones de este módulo de Flask
Lecciones de programación del módulo MVC del curso de Flask.