Using App Factory Pattern with Dash?

I was waiting for the official Dash 1.0 announcement :partying_face: thread, but I’ve not seen anything so I’ll just ask this now.

In the Dash 1.0 release notes there’s the following line:

#739 Allow the Flask app to be provided to Dash after object initialization. This allows users to define Dash layouts etc when using the app factory pattern, or any other pattern that inhibits access to the app object. This broadly complies with the flask extension API, allowing Dash to be considered as a Flask extension where it needs to be.

Does anyone have a simple example of using Dash with this “app factory pattern”? Or just in general how this would work. This sounds like it may solve some of my problems in my more complex apps, but I don’t have much familiarity with working with this sort of thing.

3 Likes

Consider an application_folder that has a ‘dash_application’ folder. The dash_application folder has a dash_example.py file as:

##########################
import glob
from pathlib import Path
from dash import Dash
import dash_table
import dash_html_components as html
import dash_core_components as dcc
import pandas as pd
from .layout import html_layout
from flask_login import login_required


def dash_application():
    # Create a Dash app
    dash_app = Dash(__name__, 
                    server=False, # Don't give dash a server just yet.
                    url_base_pathname='/dashapp/')
    

    # Create Dash Graph
    dash_app.layout = html.Div([
    html.H1('Dash application'),
    dcc.Graph(
        id='basic-graph',
        figure={
            'data':[
                {
                    'x': [0, 1],
                    'y': [0, 1],
                    'type': 'line'
                }
            ],
            'layout': {
                'title': 'Basic Graph'
                }
                }
            )
        ])

    return dash_app
##########################

The application_folder has __init__.py as:

#############################################
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, login_required
from .dash_application import dash_example


## Globally accessible libraries
db = SQLAlchemy()
login_manager = LoginManager()
login_manager.login_view = 'auth_bp.login'
dash = dash_example.dash_application()



def create_app():
    # Initialize the core application.
    app = Flask(__name__, instance_relative_config=False)
    app.config.from_object('config.Config')

    # Initialize Plugins
    db.init_app(app)
    login_manager.init_app(app)
    
    # This is where a dash app attaches to a server
    dash.init_app(app=app)

    with app.app_context():
        # Set global variables


        # Include our Routes
        from . import routes
        from . import auth


        # Register Blueprints
        app.register_blueprint(auth.auth_bp)
        app.register_blueprint(routes.main_bp)

        db.create_all()

        return app
############################################

application_folder also has routes.py

###############################
import os
from flask import Blueprint, render_template, redirect, url_for
from flask_login import current_user, login_required


main_bp = Blueprint('main_bp', __name__,
                    template_folder='templates',
                    static_folder='static')


@main_bp.route('/')
@login_required
def home():
    return render_template("index.html")


@main_bp.route('/page2')
@login_required
def home2():
    return "This is page 2"
####################################

application_folder also has auth.py

###################################
import os
from flask import Blueprint, render_template, redirect, url_for, request
from flask_login import current_user, login_required, login_user, logout_user
from werkzeug.urls import url_parse


from .models import User
from .forms import LoginForm
from . import db


auth_bp = Blueprint('auth_bp', __name__,
                    template_folder='templates',
                    static_folder='static')


@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('main_bp.home'))
    
    form = LDAPLoginForm()
    if form.validate_on_submit():
        login_user(user=form.user)
        next_page = request.args.get('next')
        if not next_page or url_parse(next_page).netloc != '':
            next_page = url_for('main_bp.home')
        return redirect(next_page)

    return render_template('login.html', title='Sign In', form=form)

@auth_bp.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for("auth_bp.login"))
##########################################

In this setup, dash is initialized but does not attach to any server. In the create_app() function, a standalone flask app/server is created and dash gets attached to this server with the .init_app call(app). I hope this is helpful.

2 Likes