Using Dash in Flask App, extend base.html using Jinja2

I want to embed several dash apps into a larger Flask application.

I’ve followed this tutorial to setup the project structure:

In the flask app I am using Jinja2 to extend each page from a base.html file to carry over the navbar, style sheets, etc.

However, when I try to extend the index_string of the dash app in a similar way I run into roadblocks.

I modified the index_string per the dash documentation: Adding CSS & JS and Overriding the Page-Load Template | Dash for Python Documentation | Plotly

I set my index_string to:

    dashapp1.index_string = '''
    {% extends "base.html" %}
    {% block content %}
        {%app_entry%}
        <footer>
            {%config%}
            {%scripts%}
            {%renderer%}
        </footer>
    {% endblock content %}
    '''

but it fails to pull in the base.html template.

Any help to resolve this would be greatly appreciated

image

1 Like

okay, so I got this to work in a roundabout way but it’s definitely very “hacky”. I’d love to hear suggestions on how to best refactor this.

I implemented a suggestion from this thread: https://github.com/plotly/dash/issues/214#issuecomment-391223557

What I did was returned my two dash apps as part of the create_app() function so I had access to them to modify the app.index() (or dashapp1.index() in my case).

I then added new app.route in my wsgi file and parsed the dashapp.index() to pull out the footer using bs4. I then injected this into a Jinja template using render_template to embed the dash app in my Flask base.html template to carryover all my navbar, css, logo, etc.

This is extremely hacky since I’ve added program logic to my wsgi file but I can’t find a better solution.

any suggestions?

.
.

wsgi.py

"""Application entry point."""
from application import create_app
from bs4 import BeautifulSoup
from flask import render_template

app, dashapp1, dashapp2 = create_app()


@app.route('/dashapp1', methods=['GET', 'POST'])
def dash_app1():
    soup = BeautifulSoup(dashapp1.index(), 'html.parser')
    footer = soup.footer
    return render_template('dash1.html', title='test', footer=footer)


@app.route('/dashapp2', methods=['GET', 'POST'])
def dash_app2():
    soup = BeautifulSoup(dashapp2.index(), 'html.parser')
    footer = soup.footer
    return render_template('dash1.html', title='test', footer=footer)


if __name__ == "__main__":
    # app.run(host='0.0.0.0', debug=True)
    app.run(debug=True)

.
.

dash1.html file

{% extends "base.html" %}
{% block content %}
    <div id="react-entry-point">
        <div class="_dash-loading">
            Loading...
        </div>
    </div>
    {{ footer|safe }}
{% endblock content %}

at least it works :sweat_smile:

Hello,

Did you fixed your problem or no? because i also faces same issue from last couple of days and can’t understand what actual going on so please help me.

I use Jinja templates to modify the index_string of my app in the dash-bootstrap-components docs.

The basic idea is to use Jinja to render the index_string and then let Dash do the rest. You do this by injecting the tags that Dash expects like {%config%} as string literals in the Jinja template.

Here is my Jinja template that will be used to create the index_string, and then here is where it gets used.

There’s some added complexity in that code because I’m creating multiple dash apps in a for loop that get stitched together with Flask, but the key idea is hopefully clear enough:

  • Create an environment with file system loader so that the templates you’re extending can be found
  • Create the template
  • Render the template with the desired arguments and assign it to the index_string property.
1 Like

So I’m doing the same except running dash under flask, setting the server setting to false for each dash instance registered. It allows me to use native flask routes. @cyotani, if you want what you have to work immediately in the flask route where you want the dash app displayed:

return render_template( ‘your_content_template’, variable_in_template=dashapp1.index())
Then in the template use {{variable_in_template|safe}} inside your normal content block. You want need any HTML like footer in the index_string, only leave the template variables.

If you want {app_entry} to show in your main block content and the other variables in your flask footer, write a function
to capture the strings separately and make them separate variables you feed in to render_template and then place those variables in the correct place inside the flask template you are calling. Also I use the same route for my dash app as the flask view route, but make sure you register the flask route/blueprints first before creating the dash apps. Also use the name parameter when creating the dash instance, it needs to be unique from any other blueprints in use or will collide.

@tcbegley thank you for sharing your method on the index_string and parsing of the layout. I like the declaritve way that dash lets you write html elements, specifically for bootstrap. I actually took the them to turn matching dash core and bootstrap elements into similar jinja macro elements, for example I can write a standard jinja template as:

{{html.Div(children=
[
bc.Button(content=“Regular”, color=“primary”, className=“mr-1”),
bc.Button(content=“Active”, color=“primary”, active=True, className=“mr-1”),
bc.Button(content=“Disabled”, color=“primary”, disabled=True),
]
)}}
I’ve used your documentation a lot to figure out all the parameters I might need in a matching jinja macro
I have a corresponding python class that matches up to my bootstrap theme and I can either render a page as either a standard flask jinja template if no reactive components, or render the complete content area as a dash app filling the content area with the same styles ect. I wish I could figure out how to put multiple dash apps on the page as react fragments. I’m playing around with Flask class based views now over routes because it seems to be a bit easier to pull everything together.