En este tutorial exploraremos los conceptos fundamentales de Flask, un framework ligero y flexible para el desarrollo web en Python. Cubriremos los siguientes temas esenciales:
- Transmisión y recepción de parámetros entre el servidor y la web
- Implementación de imágenes estáticas y subida de archivos
- Compartir la aplicación en una red local
- Herencia de plantillas HTML
- Renderizado de JavaScript y CSS
- Conexión a base de datos y creación automática de modelos
- Operaciones CRUD en la base de datos
- Interacción entre botones y funciones del servidor
- Redirección entre páginas
Configuración inicial del proyecto
Primero, veamos la estructura de archivos recomendada y las importaciones necesarias:
from flask import Flask, request, render_template
from datetime import datetime
from FlaskWebLearning import app
from flask_sqlalchemy import SQLAlchemy
import pymysql
1. Rutas y transmisión de parámetros
Veamos cómo crear rutas básicas en Flask:
@app.route('/')
@app.route('/inicio')
def inicio():
return "¡Hola mundo!"
Método 1: Parámetros en la URL
@app.route('/saludo/<tu_palabra>')
def saludar(tu_palabra):
return 'Tu mensaje: ' + tu_palabra
</tu_palabra>
Al acceder a http://127.0.0.1:5555/saludo/hola, retornará "Tu mensaje: hola".
Método 2: Renderizado de plantillas con datos
@app.route('/info/<titulo>')
def info(titulo):
return render_template("acerca.html", titulo=titulo, mensaje="Programa de prueba")
</titulo>
Plantillas HTML del proyecto
A continuación se muestran las plantillas utilizadas en esta guía:
acerca.html
{% extends "layout.html" %}
{% block content %}
<h2>{{ titulo }}.</h2>
<h3>{{ mensaje }}</h3>
<img src="{{url_for('static',filename='imagenes/6.png')}}">
<p>Utiliza esta área para proporcionar información adicional.</p>
<address>
Una Dirección Cualquiera<br />
Ciudad, CP 00000<br />
<abbr title="Teléfono">T:</abbr> 123.456.7890
</address>
<address>
<strong>Soporte:</strong> <a href="mailto:soporte@ejemplo.com">soporte@ejemplo.com</a><br />
<strong>Marketing:</strong> <a href="mailto:marketing@ejemplo.com">marketing@ejemplo.com</a>
</address>
{% endblock %}
formulario.html
{% extends "layout.html" %}
{% block content %}
<form action="{{ url_for('formulario') }}" method="post">
<label for="usuario">Usuario</label><br>
<input type="text" name="usuario" placeholder="Nombre de usuario"><br>
<label for="contrasena">Contraseña</label><br>
<input type="password" name="contrasena" placeholder="Contraseña"><br>
<input type="submit" name="submit" value="Iniciar sesión">
</form>
<a href="https://ejemplo.com">Ir a sitio externo</a>
<a href="/enlace">Redirección interna</a>
<address>
Una Dirección Cualquiera<br />
Ciudad, CP 00000<br />
<abbr title="Teléfono">T:</abbr> 123.456.7890
</address>
<address>
<strong>Soporte:</strong> <a href="mailto:soporte@ejemplo.com">soporte@ejemplo.com</a><br />
<strong>Marketing:</strong> <a href="mailto:marketing@ejemplo.com">marketing@ejemplo.com</a>
</address>
{% endblock %}
index.html
{% extends "layout.html" %}
{% block content %}
<div class="jumbotron">
<h1>Flask</h1>
<p class="lead">Flask es un framework web gratuito para construir excelentes sitios y aplicaciones web.</p>
<p><a href="http://flask.pocoo.org/" class="btn btn-primary btn-large">Saber más »</a></p>
</div>
<div>
<form method="post" action="/subir_foto" enctype="multipart/form-data">
<input type="file" size="30" name="foto" />
<br>
<input type="text" class="txt_input" name="nombre" style="margin-top:15px;" />
<input type="submit" value="Enviar información" class="button-new" style="margin-top:15px;" />
</form>
</div>
<div class="row">
<div class="col-md-4">
<h2>Comenzar</h2>
<p>Flask te ofrece una forma poderosa de construir sitios web dinámicos.</p>
<p><a class="btn btn-default" href="http://flask.pocoo.org/docs/">Saber más »</a></p>
</div>
<div class="col-md-4">
<h2>Más bibliotecas</h2>
<p>El Índice de Paquetes Python es un repositorio de software.</p>
<p><a class="btn btn-default" href="https://pypi.python.org/pypi">Saber más »</a></p>
</div>
<div class="col-md-4">
<h2>Azure</h2>
<p>Puedes publicar fácilmente en Microsoft Azure.</p>
<p><a class="btn btn-default" href="http://azure.microsoft.com">Saber más »</a></p>
</div>
</div>
{% endblock %}
layout.html (Plantilla base)
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ titulo }} - Mi Aplicación Flask</title>
<link rel="stylesheet" type="text/css" href="/static/content/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="/static/content/site.css" />
<script src="/static/scripts/modernizr-2.6.2.js"></script>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="/" class="navbar-brand">Nombre de App</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
</ul>
</div>
</div>
</div>
<div class="container body-content">
{% block content %}{% endblock %}
<hr />
<footer>
<p>© {{ year }} - Mi Aplicación Flask</p>
</footer>
</div>
<script src="/static/scripts/jquery-1.10.2.js"></script>
<script src="/static/scripts/bootstrap.js"></script>
<script src="/static/scripts/respond.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>
persona.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ titulo }} - Mi Aplicación Flask</title>
<link rel="stylesheet" type="text/css" href="/static/content/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="/static/content/site.css" />
<script src="/static/scripts/modernizr-2.6.2.js"></script>
</head>
<body>
<h2>Nombre: {{persona.nombre}}</h2>
<h2>Edad: {{persona.edad}}</h2>
<div class="container body-content">
{% block content %}{% endblock %}
<hr />
<footer>
<p>© {{ year }} - Mi Aplicación Flask</p>
</footer>
</div>
<script src="/static/scripts/jquery-1.10.2.js"></script>
<script src="/static/scripts/bootstrap.js"></script>
<script src="/static/scripts/respond.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>
filtro.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ titulo }} - Mi Aplicación Flask</title>
<link rel="stylesheet" type="text/css" href="/static/content/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="/static/content/site.css" />
<script src="/static/scripts/modernizr-2.6.2.js"></script>
</head>
<body>
<h2>{{texto|formatoid}}</h2>
<div class="container body-content">
{% block content %}{% endblock %}
<hr />
<footer>
<p>© {{ year }} - Mi Aplicación Flask</p>
</footer>
</div>
<script src="/static/scripts/jquery-1.10.2.js"></script>
<script src="/static/scripts/bootstrap.js"></script>
<script src="/static/scripts/respond.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>
edad.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ titulo }} - Mi Aplicación Flask</title>
<link rel="stylesheet" type="text/css" href="/static/content/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="/static/content/site.css" />
<script src="/static/scripts/modernizr-2.6.2.js"></script>
</head>
<body>
{%if edad<18 %}
<p>No ha cumplido 18 años, puede navegar por internet</p>
{% else %}
<p>Ya tiene 18 años, no puede acceder a este sitio</p>
{% endif %}
<div class="container body-content">
{% block content %}{% endblock %}
<hr />
<footer>
<p>© {{ year }} - Mi Aplicación Flask</p>
</footer>
</div>
<script src="/static/scripts/jquery-1.10.2.js"></script>
<script src="/static/scripts/bootstrap.js"></script>
<script src="/static/scripts/respond.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>
También podemos enviar objetos a las plantillas:
@app.route('/persona')
def persona():
p = Persona("juan", 25)
return render_template("persona.html", persona=p)
Filtros de plantilla
Los filtros son muy útiles para transformar datos en las plantillas:
def formatear_datos(valor):
return valor[2:5]
app.add_template_filter(formatear_datos, 'formatoid')
@app.route('/filtro/<s>')
def filtrar(s):
return render_template("filtro.html", texto=s)
</s>
2. Imágenes estáticas y subida de archivos
Insertar imágenes estáticas:
<img src="{{url_for('static',filename='imagenes/6.png')}}">
Subida de archivos:
@app.route('/principal')
def principal():
return render_template("index.html")
@app.route('/subir_foto', methods=['post'])
def subir_foto():
imagen = request.files.get('foto')
nombre_usuario = request.form.get("nombre")
ruta = r"FlaskWebLearning/static/imagenes/"
ruta_archivo = ruta + imagen.filename
imagen.save(ruta_archivo)
print('Foto subida exitosamente, usuario:', nombre_usuario)
return render_template('index.html')
Primero accede a la ruta /principal para ver el formulario de subida.
3. Compartir aplicación en red local
Para hacer accesible tu aplicación en la red local, utiliza el parámetro host:
"""
Archivo principal de la aplicación
"""
from os import environ
from FlaskWebLearning import app
if __name__ == '__main__':
try:
PUERTO = int(environ.get('SERVER_PORT', '5555'))
except ValueError:
PUERTO = 5555
app.run(host='0.0.0.0', port=PUERTO)
4. Herencia de plantillas
La herencia de plantillas permite reutilizar código HTML:
{% extends "layout.html" %}
{% extends "layout.html" %}
{% block content %}
<h2>{{ titulo }}.</h2>
<h3>{{ mensaje }}</h3>
...
{% endblock %}
El bloque {% block content %}{% endblock %} en las plantillas derivadas reemplaza el contenido correspondiente en layout.html.
5. Renderizado de CSS y JavaScript
La inclusión de archivos estáticos en layout.html:
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ titulo }} - Mi Aplicación Flask</title>
<link rel="stylesheet" type="text/css" href="/static/content/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="/static/content/site.css" />
<script src="/static/scripts/modernizr-2.6.2.js"></script>
</head>
6. Conexión a base de datos y modelos
Configuración de la base de datos:
pymysql.install_as_MySQLdb()
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://usuario:contraseña@127.0.0.1:3306/nombre_db?charset=utf8'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
Definición de modelos:
class Usuario(db.Model):
__tablename__ = 'usuario'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
nombre = db.Column(db.String(100))
contrasena = db.Column(db.String(100))
class Articulo(db.Model):
__tablename__ = 'articulo'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
titulo = db.Column(db.String(200), nullable=False)
contenido = db.Column(db.Text, nullable=False)
autor_id = db.Column(db.Integer, db.ForeignKey("usuario.id"))
autor = db.relationship('Usuario', backref='articulos')
Creación automática de tablas:
with app.app_context():
db.create_all()
7. Operaciones CRUD en la base de datos
Crear (Insert):
@app.route('/usuario/agregar')
def agregar_usuario():
usuario1 = Usuario(nombre="Carlos", contrasena="123456")
usuario2 = Usuario(nombre="Ana", contrasena="123456")
usuario3 = Usuario(nombre="Pedro", contrasena="123456")
db.session.add(usuario1)
db.session.add(usuario2)
db.session.add(usuario3)
db.session.commit()
return "Usuarios insertados correctamente"
Eliminar (Delete):
@app.route('/usuario/eliminar')
def eliminar_usuario():
usuario1 = db.session.get(Usuario, 1)
print(usuario1)
db.session.delete(usuario1)
db.session.commit()
return "Usuario eliminado correctamente"
Actualizar (Update):
@app.route('/usuario/actualizar')
def actualizar_usuario():
usuarios = Usuario.query.filter_by(nombre='Ana')
for usuario in usuarios:
usuario.contrasena = 'nueva_contrasena'
db.session.commit()
return "Usuario actualizado correctamente"
Consultar (Read):
@app.route('/usuario/consultar')
def consultar_usuario():
usuario1 = db.session.get(Usuario, 1)
print(usuario1)
usuarios = Usuario.query.filter_by(nombre='Ana')
print(usuarios)
usuarios = Usuario.query.all()
resultado = ''
for i in usuarios:
print(i.nombre, i.contrasena)
resultado += i.nombre + ' ' + i.contrasena + '\n'
db.session.commit()
return resultado
8. Interacción entre botones y funciones
@app.route('/pagina', methods=['GET', 'POST'])
def pagina():
return render_template("formulario.html")
@app.route('/formulario', methods=['GET', 'POST'])
def formulario():
if request.method == 'POST':
nombre = request.form['usuario']
contrasena = request.form['contrasena']
print(nombre, contrasena)
nuevo_usuario = Usuario(nombre=nombre, contrasena=contrasena)
db.session.add(nuevo_usuario)
db.session.commit()
return "Datos guardados correctamente"
return "Hola"
El formulario HTML correspondiente:
{% extends "layout.html" %}
{% block content %}
<form action="{{ url_for('formulario') }}" method="post">
<label for="usuario">Usuario</label><br>
<input type="text" name="usuario" placeholder="Nombre de usuario"><br>
<label for="contrasena">Contraseña</label><br>
<input type="password" name="contrasena" placeholder="Contraseña"><br>
<input type="submit" name="submit" value="Iniciar sesión">
</form>
<a href="https://ejemplo.com">Ir a sitio externo</a>
<a href="/enlace">Redirección interna</a>
<address>
Una Dirección Cualquiera<br />
Ciudad, CP 00000<br />
<abbr title="Teléfono">T:</abbr> 123.456.7890
</address>
<address>
<strong>Soporte:</strong> <a href="mailto:soporte@ejemplo.com">soporte@ejemplo.com</a><br />
<strong>Marketing:</strong> <a href="mailto:marketing@ejemplo.com">marketing@ejemplo.com</a>
</address>
{% endblock %}
Accede primero a la ruta /pagina, completa el formulario y presiona "Iniciar sesión". Los datos se guardarán en la base de datos.