Despacho de aplicaciones

El despacho de aplicaciones es el proceso de combinar múltiples aplicaciones Flask en el nivel WSGI. Puedes combinar no sólo aplicaciones Flask sino cualquier aplicación WSGI. Esto te permitiría ejecutar una aplicación Django y una aplicación Flask en el mismo intérprete una al lado de la otra si lo deseas. La utilidad de esto depende de cómo funcionen las aplicaciones internamente.

La diferencia fundamental con Grandes aplicaciones como paquetes es que en este caso estás ejecutando la misma o diferentes aplicaciones Flask que están completamente aisladas entre sí. Ejecutan configuraciones diferentes y se despachan en el nivel WSGI.

Trabajar con este documento

Cada una de las técnicas y ejemplos que aparecen a continuación dan como resultado un objeto aplicación que puede ejecutarse con cualquier servidor WSGI. Para producción, vea Despliegue en producción. Para desarrollo, Werkzeug proporciona un servidor a través de werkzeug.serving.run_simple():

from werkzeug.serving import run_simple
run_simple('localhost', 5000, application, use_reloader=True)

Tenga en cuenta que run_simple no está pensado para su uso en producción. Utilice un servidor WSGI de producción. Consulte Despliegue en producción.

Para poder utilizar el depurador interactivo, la depuración debe estar habilitada tanto en la aplicación como en el servidor simple. Aquí está el ejemplo de «hola mundo» con depuración y run_simple:

from flask import Flask
from werkzeug.serving import run_simple

app = Flask(__name__)
app.debug = True

@app.route('/')
def hello_world():
    return 'Hello World!'

if __name__ == '__main__':
    run_simple('localhost', 5000, app,
               use_reloader=True, use_debugger=True, use_evalex=True)

Combinación de aplicaciones

Si tienes aplicaciones completamente separadas y quieres que funcionen una al lado de la otra en el mismo proceso de interpretación de Python puedes aprovechar la werkzeug.wsgi.DispatcherMiddleware. La idea aquí es que cada aplicación Flask es una aplicación WSGI válida y son combinadas por el middleware despachador en una más grande que es despachada basada en el prefijo.

Por ejemplo, podrías tener tu aplicación principal ejecutada en / y tu interfaz backend en /backend:

from werkzeug.middleware.dispatcher import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend

application = DispatcherMiddleware(frontend, {
    '/backend': backend
})

Despacho por subdominio

A veces puedes querer usar múltiples instancias de la misma aplicación con diferentes configuraciones. Asumiendo que la aplicación es creada dentro de una función y que puedes llamar a esa función para instanciarla, eso es realmente fácil de implementar. Para desarrollar tu aplicación de forma que soporte la creación de nuevas instancias en funciones echa un vistazo al patrón Fábricas de aplicaciones.

Un ejemplo muy común sería crear aplicaciones por subdominio. Por ejemplo, configuras tu servidor web para que envíe todas las peticiones de todos los subdominios a tu aplicación y luego utilizas la información del subdominio para crear instancias específicas para cada usuario. Una vez que tengas tu servidor configurado para escuchar en todos los subdominios puedes usar una aplicación WSGI muy simple para hacer la creación de aplicaciones dinámicas.

El nivel perfecto de abstracción en ese sentido es la capa WSGI. Escribes tu propia aplicación WSGI que mira la petición que llega y la delega a tu aplicación Flask. Si esa aplicación no existe todavía, se crea dinámicamente y se recuerda:

from threading import Lock

class SubdomainDispatcher(object):

    def __init__(self, domain, create_app):
        self.domain = domain
        self.create_app = create_app
        self.lock = Lock()
        self.instances = {}

    def get_application(self, host):
        host = host.split(':')[0]
        assert host.endswith(self.domain), 'Configuration error'
        subdomain = host[:-len(self.domain)].rstrip('.')
        with self.lock:
            app = self.instances.get(subdomain)
            if app is None:
                app = self.create_app(subdomain)
                self.instances[subdomain] = app
            return app

    def __call__(self, environ, start_response):
        app = self.get_application(environ['HTTP_HOST'])
        return app(environ, start_response)

Este despachador puede usarse así:

from myapplication import create_app, get_user_for_subdomain
from werkzeug.exceptions import NotFound

def make_app(subdomain):
    user = get_user_for_subdomain(subdomain)
    if user is None:
        # if there is no user for that subdomain we still have
        # to return a WSGI application that handles that request.
        # We can then just return the NotFound() exception as
        # application which will render a default 404 page.
        # You might also redirect the user to the main page then
        return NotFound()

    # otherwise create the application for the specific user
    return create_app(user)

application = SubdomainDispatcher('example.com', make_app)

Despacho por Ruta

La distribución por una ruta en la URL es muy similar. En lugar de mirar la cabecera Host para averiguar el subdominio simplemente se mira la ruta de la petición hasta la primera barra:

from threading import Lock
from werkzeug.wsgi import pop_path_info, peek_path_info

class PathDispatcher(object):

    def __init__(self, default_app, create_app):
        self.default_app = default_app
        self.create_app = create_app
        self.lock = Lock()
        self.instances = {}

    def get_application(self, prefix):
        with self.lock:
            app = self.instances.get(prefix)
            if app is None:
                app = self.create_app(prefix)
                if app is not None:
                    self.instances[prefix] = app
            return app

    def __call__(self, environ, start_response):
        app = self.get_application(peek_path_info(environ))
        if app is not None:
            pop_path_info(environ)
        else:
            app = self.default_app
        return app(environ, start_response)

La gran diferencia entre ésta y la del subdominio es que ésta retrocede a otra aplicación si la función del creador devuelve None:

from myapplication import create_app, default_app, get_user_for_prefix

def make_app(prefix):
    user = get_user_for_prefix(prefix)
    if user is not None:
        return create_app(user)

application = PathDispatcher(default_app, make_app)