dash_on_flask icon indicating copy to clipboard operation
dash_on_flask copied to clipboard

Integrate the dash app layout with templates?

Open r-chris opened this issue 5 years ago • 4 comments

Is there any way to integrate the dash app into one of the templates? For example I may have some common elements such as a navbar / footer that should also be displayed around the dash_app. Any advice on how to do this or would we just have to duplicate that part of the HTML code in the layout.py?

r-chris avatar Feb 08 '20 09:02 r-chris

I actually was wondering this same thing. Here's how I made it work.

The following modifies the "index_string" per this article. https://dash.plot.ly/external-resources

from flask import Flask, render_template_string

def _get_index_string(template):
    """
    Replace the following 'commented-out' placeholders in my 'dash.html' 
    Flask template with the placeholders that Dash requires, for modifying the 
    'index_string' of the dash app
    """
    template = template.replace(r'<!-- %metas% -->', r'{%metas%}')
    template = template.replace(r'<!-- %title% -->', r'{%title%}')
    template = template.replace(r'<!-- %favicon% -->', r'{%favicon%}')
    template = template.replace(r'<!-- %css% -->', r'{%css%}')
    template = template.replace(r'<!-- %app_entry% -->', r'{%app_entry%}')
    template = template.replace(r'<!-- %config% -->', r'{%config%}')
    template = template.replace(r'<!-- %scripts% -->', r'{%scripts%}}')
    template = template.replace(r'<!-- %renderer% -->', r'{%renderer%}')

    return template
def register_dashapps(app):
    """
    Register Dash apps with the Flask app
    """

    # Meta tags for viewport responsiveness
    meta_viewport = {
        "name": "viewport", 
        "content": "width=device-width, initial-scale=1, shrink-to-fit=no"
    }

    dashapp1 = dash.Dash(__name__,
        server=app,
        url_base_pathname='/dash/',
        assets_folder=get_root_path(__name__) + '/assets/',
        meta_tags=[meta_viewport], 
    )

    with app.app_context():
        # Render my Flask template to get a special 'index_string' for the Dash app
        path = get_root_path(__name__) + "/templates/dash.html"
        with open(path, 'r') as f:
            template_string = render_template_string(f.read())
        index_string = _get_index_string(template_string)
        dashapp1.index_string = index_string

        dashapp1.title = 'CHANGING THE LANDSCAPE'
        dashapp1.layout = get_layout(dashapp1)
        register_callbacks(dashapp1)

    _protect_dashviews(dashapp1)

    return app

I also needed to manually set the "SERVER_NAME" environment variable, so that flask could use the "render_template_string()" method

class BaseConfig:
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SECRET_KEY = os.environ['SECRET_KEY']

    # Change this for production. Flask needs this if we use the 'render_template_string()' method
    SERVER_NAME = '127.0.0.1:5000'

I hope the above is relatively self-explanatory. I assume you're way better at Flask than I am. :)

Cheers, Sean

mccarthysean avatar Feb 08 '20 15:02 mccarthysean

Thanks @mccarthysean that is very insightful - I will try this as soon as possible.

r-chris avatar Feb 08 '20 17:02 r-chris

@mccarthysean thanks for the contribution. I will have some time to review it next week and will add a section in the blog/template :)

okomarov avatar Feb 11 '20 14:02 okomarov

Thank you @mccarthysean for the helpful advice.

One small addition I had to make was to register the dashapps after the blueprints to get commands like url_for('main.index') in the template to work:

def create_app():
    server = Flask(__name__)
    server.config.from_object(BaseConfig)

    register_extensions(server)
    register_blueprints(server)
    register_dashapps(server)

    return server

I just have one issue that I cannot resolve: The base.html in the repository uses the variable current_user to link to either "login" or "logout". I am trying to use this logic in a navigation bar that should be displayed on all pages, also on the dashboard pages. Therefore, I import current_user when I create the dash app and pass it as an argument to render_template_string():

from flask_login import current_user
.
.
.
with open(path, 'r') as f:
    template_string = render_template_string(
        f.read(),
        current_user=current_user
    )

However, the current_user is always None in this context, whereas, for example, on the index page it works as intended. Does anyone know a solution to that?

reiseb avatar Mar 22 '20 18:03 reiseb