Tutorial de introducción a Flask: Fundamentos del framework web Python

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 &raquo;</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 &raquo;</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 &raquo;</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 &raquo;</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>&copy; {{ 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>&copy; {{ 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>&copy; {{ 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>&copy; {{ 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.

Etiquetas: Python Flask web-development SQLAlchemy Jinja2

Publicado el 6-10 17:06