Fábricas de aplicaciones

Si ya estás usando paquetes y blueprints para tu aplicación (Aplicaciones modulares con Blueprints) hay un par de formas muy buenas de mejorar la experiencia. Un patrón común es crear el objeto de la aplicación cuando se importa el blueprint. Pero si mueves la creación de este objeto a una función, puedes crear múltiples instancias de esta aplicación más tarde.

Entonces, ¿por qué querrías hacer esto?

  1. Pruebas. Puedes tener instancias de la aplicación con diferentes configuraciones para probar cada caso.

  2. Múltiples instancias. Imagina que quieres ejecutar diferentes versiones de la misma aplicación. Por supuesto, usted podría tener múltiples instancias con diferentes configuraciones establecidas en su servidor web, pero si usted utiliza las fábricas, puede tener múltiples instancias de la misma aplicación que se ejecuta en el mismo proceso de aplicación que puede ser útil.

Entonces, ¿cómo se implementa esto?

Fábricas básicas

La idea es configurar la aplicación en una función. Así:

def create_app(config_filename):
    app = Flask(__name__)
    app.config.from_pyfile(config_filename)

    from yourapplication.model import db
    db.init_app(app)

    from yourapplication.views.admin import admin
    from yourapplication.views.frontend import frontend
    app.register_blueprint(admin)
    app.register_blueprint(frontend)

    return app

El inconveniente es que no se puede utilizar el objeto de aplicación en los blueprints en el momento de la importación. Sin embargo, puede utilizarlo desde una solicitud. ¿Cómo se accede a la aplicación con la configuración? Utiliza current_app:

from flask import current_app, Blueprint, render_template
admin = Blueprint('admin', __name__, url_prefix='/admin')

@admin.route('/')
def index():
    return render_template(current_app.config['INDEX_TEMPLATE'])

Aquí buscamos el nombre de una plantilla en la configuración.

Fábricas y extensiones

Es preferible crear tus extensiones y fábricas de aplicaciones de manera que el objeto de la extensión no se vincule inicialmente a la aplicación.

Usando Flask-SQLAlchemy, como ejemplo, no deberías hacer algo parecido a esto:

def create_app(config_filename):
    app = Flask(__name__)
    app.config.from_pyfile(config_filename)

    db = SQLAlchemy(app)

Pero, más bien, en model.py (o equivalente):

db = SQLAlchemy()

y en su aplicacion.py (o equivalente):

def create_app(config_filename):
    app = Flask(__name__)
    app.config.from_pyfile(config_filename)

    from yourapplication.model import db
    db.init_app(app)

Utilizando este patrón de diseño, no se almacena ningún estado específico de la aplicación en el objeto de extensión, por lo que un objeto de extensión puede ser utilizado para múltiples aplicaciones. Para más información sobre el diseño de las extensiones, consulte Desarrollo de extensiones de Flask.

Uso de aplicaciones

Para ejecutar una aplicación de este tipo, puede utilizar el comando flask:

$ flask run --app hello run

Flask detectará automáticamente la fábrica si se llama create_app o make_app en hello. También puedes pasar argumentos a la fábrica así:

$ flask run --app hello:create_app(local_auth=True)``

Entonces se llama a la fábrica create_app en myapp con el argumento clave local_auth=True. Consulta Interfaz de línea de comandos para más detalles.

Mejoras en la fábrica

La función de fábrica anterior no es muy inteligente, pero se puede mejorar. Los siguientes cambios son sencillos de implementar:

  1. Hacer posible pasar valores de configuración para las pruebas unitarias para no tener que crear archivos de configuración en el sistema de archivos.

  2. Llamar a una función desde un blueprint cuando la aplicación se está configurando para tener un lugar donde modificar los atributos de la aplicación (como enganchar los manejadores de peticiones antes/después, etc.)

  3. Añadir middlewares WSGI cuando la aplicación se está creando si es necesario.