Flask

Iniciar la aplicación

Crea una carpeta llamada app, que alojará la aplicación.

(venv) $ mkdir app

Dentro crea un fichero con nombre __init__.py con el siguiente código:

from flask import Flask

app = Flask(__name__)

from app import routes
  • Se crea el objeto de la aplicación como una instancia de una clase Flask importada del paquete flask.
  • La variable __name__ que se pasa a la clase Flask es una variable predefinida de Python, cuyo nombre corresponde al del módulo en el que se utiliza.
  • En la práctica, pasar esta variable __name__ casi siempre configurará Flask correctamente. A continuación, la aplicación importa el módulo routes, que aún no existe.

Un aspecto que puede resultar confuso al principio es que existen dos entidades con el mismo nombre app.

  • El paquete app que se define mediante el directorio de la aplicación y el script __init__.py , y se hace referencia a él en la declaración from app import routes.
  • La variable app que se define como una instancia de la clase Flask en el script __init__.py , lo que la convierte en miembro del paquete app.

Otra particularidad es que el módulo routes se importa al final del script y no al principio, como se hace habitualmente. La importación al final es una solución conocida que evita las importaciones circulares , un problema común en las aplicaciones Flask. Verás que el módulo routes necesita importar la variable app definida en este script, por lo que colocar una de las importaciones recíprocas al final evita el error que resulta de las referencias mutuas entre estos dos archivos.

Modulo routes

Las rutas gestionan las diferentes URL que admite la aplicación. En Flask, los manejadores de las rutas de la aplicación se escriben como funciones de Python, llamadas view funtions. Estas funciones se asocian a una o más URL de ruta para que Flask sepa qué lógica ejecutar cuando un cliente solicita una URL determinada.

Aquí está la primera función de vista para esta aplicación, que debes escribir en un nuevo módulo llamado routes.py

from app import app

@app.route('/')
@app.route('index')

def index():
  return "Hello, World"

Las dos líneas @app.route son decoradores , una característica única del lenguaje Python. Un decorador modifica la función que le sigue. Un patrón común con los decoradores es usarlos para registrar funciones como devoluciones de llamada para ciertos eventos.

En este caso, el decorador @app.route crea una asociación entre la URL proporcionada como argumento y la función. Esto significa que cuando un navegador web solicita cualquiera de estas dos URL, Flask invocará esta función y devolverá su valor de retorno al navegador como respuesta.

Para completar la aplicación, necesitas tener un script de Python en el nivel superior que defina la instancia de la aplicación Flask. Llamemos a este script microblog.py y definámoslo como una sola línea que importe la instancia de la aplicación:

from app import app

¿Recuerdas las dos entidades app? Aquí puedes verlas juntas en la misma oración.

  • La instancia de la aplicación Flask se llama app y pertenece al paquete app.
  • La instrucción from app import app importa la variable app que pertenece al paquete app. Si te resulta confuso, puedes cambiar el nombre del paquete o de la variable.

Para asegurarnos de que todo se está haciendo correctamente, a continuación se muestra un diagrama de la estructura del proyecto hasta el momento:

microblog/
  venv/
  app/
    __init__.py
    routes.py
  microblog.py

La aplicación está lista, solo hace falta llamarla:

flask --app microblog.py --debug run

Templates

Las plantillas ayudan a lograr la separación entre la presentación y la lógica de negocio. En Flask, las plantillas se escriben como archivos separados, almacenados en una carpeta llamada templates dentro del paquete de la aplicación.

archivo index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
        <title>{{title}}</title>
    </head>
    <body>
        <h1>Hello, {{user.username}}!</h1>
    </body>
</html>

Los marcadores de posición {{ … }} representan las partes de la página que son variables y se cargan en el momento de la ejecución. Ahora que usamos las plantillas, podemos eliminar el texto de la función index() y agregar la plantilla.

from flask import render_template
from app import app

@app.route('/')
@app.route('/index')
def index():
    user = {'username': 'Miguel'}
    return render_template('index.html', title='Home', user=user)

La operación que convierte una plantilla en una página HTML completa se llama renderizado . Para renderizar la plantilla, importa una función Flask llamada  render_template(). Esta función realiza esta acción:

  1. Busca el archivo templates/index.html
  2. Sustituye las variables Jinja2
  3. Devuelve el HTML generado al navegador

La función render_template() invoca el motor de plantillas Jinja que viene incluido con el framework Flask. Jinja sustituye  los bloques {{ ... }} con los valores correspondientes, proporcionados por los argumentos en la llamada  render_template().

Declaraciones condicionales

Ya has visto cómo Jinja reemplaza los marcadores de posición con valores reales durante la renderización, pero esta es solo una de las muchas operaciones potentes que Jinja admite en los archivos de plantilla. Por ejemplo, las plantillas también admiten instrucciones de control, que se encuentran dentro {% ... %}de bloques. La siguiente versión de la plantilla index.html añade una instrucción condicional:

<!doctype html>
<html>
    <head>
        {% if title %}
        <title>{{ title }} - Microblog</title>
        {% else %}
        <title>Welcome to Microblog!</title>
        {% endif %}
    </head>
    <body>
        <h1>Hello, {{ user.username }}!</h1>
    </body>
</html>

Bucles

Hasta este punto, la plantilla index.html mostraba únicamente información estática o variables individuales, como el nombre del usuario conectado. Sin embargo, en una aplicación real es habitual trabajar con colecciones de datos, por ejemplo:

  • Publicaciones de un blog.
  • Comentarios.
  • Productos.
  • Resultados de consultas.
  • Registros de una base de datos.

Para representar este tipo de estructuras, Jinja2 incorpora instrucciones de control similares a las de Python, entre ellas el bucle for.

En app/routes.py, debajo del usuario autenticado, crea una lista llamada posts. Cada elemento de la lista es un diccionario que representa una publicación con autor:body . A la función añade posts=posts

posts = [
    {
        'author': {'username': 'John'},
        'body': 'Beautiful day in Portland!'
    },
    {
        'author': {'username': 'Susan'},
        'body': 'The Avengers movie was so cool!'
    }
]

Uso del bucle for en Jinja2

En templates/index.html, la colección se recorre mediante la instrucción:

{% for post in posts %}
    ...
{% endfor %}

Durante cada iteración, post representa un elemento individual de la lista al que se puede acceder a sus atributos o claves.

Acceso a estructuras anidadas

Jinja2 permite navegar por diccionarios anidados usando la notación de punto, lo que hace el código más legible.

{{ post.author.username }}
{{ post.body }}

Herencia de plantillas en Jinja2

La idea consiste en crear una plantilla base que contenga toda la estructura común del sitio y dejar definidos ciertos espacios vacíos, llamados bloques, donde cada página insertará su contenido específico.

La plantilla base

La plantilla base.html define la estructura general de todas las páginas.

El bloque content

La instrucción define una zona reemplazable. La plantilla hija podrá proporcionar el contenido que se insertará exactamente en ese punto.

{% block content %}{% endblock %}

Plantilla hija: index.html

{% extends "base.html" %}

{% block content %}
    <h1>Hi, {{ user.username }}!</h1>

    {% for post in posts %}
        <div>
            <p>
                {{ post.author.username }} says:
                <b>{{ post.body }}</b>
            </p>
        </div>
    {% endfor %}
{% endblock %}

La instrucción extends Indica que index.html no es un documento HTML completo, sino una plantilla que hereda de base.html.

{% extends "base.html" %}

Cuando Flask ejecuta: render_template("index.html"), Jinja2 realiza internamente estos pasos:

  1. Carga index.html.
  2. Detecta {% extends "base.html" %}.
  3. Carga base.html.
  4. Sustituye el bloque content de base.html por el bloque content definido en index.html.
  5. Genera el HTML final.