Cracking Full Control Over Plot.ly Dash
Tags: plotly, flask, datavis
Published: 2019-01-22 18:13:28

---
title: Cracking Full Control Over Plot.ly Dash
published: true
tags: Plotly,Flask,Data Vis
canonical_url: https://hackersandslackers.com/gaining-full-control-over-plotly-dash/
---

 ![Cracking Full Control Over Plot.ly Dash](http://res-3.cloudinary.com/hackers-and-slackers/image/upload/q_auto/v1/images/Dash.jpg)

Ahh, [**Plot.ly**](http://plot.ly/); typing that name into a post headline triggers an emotional cocktail of pride and embarrassment. Over the years Plotly has been at the core of some of the most influential products I’ve worked on: a hodgepodge of Fintech and humanitarian clients, all of which are still proudly waving their charts and dashboards around the world. Yet, my mind is boggled by a simple question: what the hell took us so long to write our first post about Plotly? We've been operating Hackers and Slackers for over a full year now... did I seriously write a [post about JQuery](https://hackersandslackers.com/making-ajax-calls-with-jquery/) in that time before reaching this point?

Much has changed in the last year or so for our friends in Montreal. Number 1 in my book is the price reduction of their core product: from _300 dollars_ to _zero_. I paid the 300 dollars. We really need to get a “donate” button around here. 

A close second is undoubtedly the introduction of **Plot.ly Dash**. Dash tickles a sentiment which has danced through many young and helplessly naïve Pythonistas' minds: _what if we could write_ **_only_** _in Python, like,_ **_forever_** _?_ As awful of an idea it is to start Googling Python-to-frontend code interpreters (they exist; I checked), Plotly's Dash does a shockingly good job of breathing life into that romantic fantasy of committing to Python forever.

But we're not here to deliver a recycled _'What is Plotly?'_ synopsis. We're not even interested in the obligatory _'How to Get Started Using This Already-Well-Documented-Technology'_ post. Plotly deserves better than that. Instead, we're coming hot out of the gate swinging: we're going to show you how to beat Plotly down, break it, and make it bend to your will. Welcome to a magical edition of Hacking Plotly. It must be Christmas, folks.

## Let's Make a Plotly + Flask Lovechild from Hell

Like almost every single advancement to come out of Python-geared architecture this year, Dash has a little secret: it's gotten here with a little help from Flask. Alright, perhaps more than a little: Dash actually extends Flask. Sounds sensible, and perhaps even exciting at first; its almost as though every crush you've ever had decided it be best to simply put their differences aside to start a group chat with you in the interest of making your sexual well-being an equal team effort out of sheer love. As you've already guessed, life doesn't work like that.

Dash hijacks Flask from the beginning, starting with the way we instantiate the app. Any code monkey who has laid eyes upon a **wsgi.py** file can tell you something is up before you can even say `app = dash.Dash( __name__ )`. Check out the recommended startup boilerplate:

```
from dash import Dash
import dash_core_components as dcc
import dash_html_components as html

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = Dash( __name__ , external_stylesheets=external_stylesheets)

app.layout = html.Div(
        id='example-div-element'
        )

if __name__ == ' __main__':
    app.run_server(debug=True)
```

If you were to attempt to take this boilerplate and attempt to add core Flask logic, such as authentication with `Flask-Login`, generating assets with `Flask-Assets`, or just creating a global database, where would you start? Plot.ly cleverly suggests reserving the `app` namespace for your app- the very same that we would do with Flask. Yet if we attempt to modify the `app` object the same as we would with Flask, nothing will work: Dash has declared an ecosystem, and nowhere in that ecosystem are you invited to add custom Flask application logic out of the box.

Dash does what it was intended to do very well: building dashboard-based applications. The issue is that applications which can _only display data_ aren't entirely useful as end products. What if we wanted to create a fully-featured app, where data visualization was simply a feature of said app?

## Creating a Fully-Featured App where Data Vis is Simply a Feature of Said App

A common "workaround" you'll find in the community is passing Flask to Dash as the underlying "server", something like this:

```
from flask import Flask
from dash import Dash
import dash_core_components as dcc
import dash_html_components as html

server = Flask( __name__ )
app = dash.Dash( __name__ , server=server, url_base_pathname='/path')
app.layout = html.Div(id='example-div-element')

@server.route("/dash")
def MyDashApp():
    return app.index()
```

Make no mistake: this method _sucks._ Sure, you've regained the ability to create routes here and there, but let's not forget:

- Your app will always start on a Dash-served page: if anything, we'd want our start page to be something we have full control over to then dive into the Dash components.
- Access to globally available Flask plugins are still unavailable in this method. Notice how we never set an application context?
- Your ability to style your application with static assets and styles is completely out of your hands.
- Container architecture built on Flask, such as Google App Engine, won't play nicely when we start something that isn't Flask. So there's a good chance that playing by the rules means losing the ability to deploy.

If we want to do these things, we cannot start our app as an instance of Dash and attempt to work around it. Instead, we must create a Flask app, and put Dash in its place as an app embedded in _our_ app. This gives us full control over when users can enter the Dash interface, and even within that interface, we can still manage database connections or user sessions as we see fit. Welcome to the big leagues.

## Turning the Tables: Dash Inside Flask

First things first, let's get our **wsgi.py** file back. Pretty much any hosted Python application expects this, so enough with the **app.py** nonsense.

```
from application import create_app

app = create_app()

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

Look familiar? Not only do we get Flask back, but we get our entire application factory and all that it includes. Take a look at _application/\_\_init\_\_.py:_

```
from flask import Flask, g
from flask_sqlalchemy import SQLAlchemy
from flask_redis import FlaskRedis
from . import dashview

def create_app():
    """Construct the core application."""
    app = Flask( __name__ , instance_relative_config=False)
    app.config.from_object('config.Config')
    dash_app = dashview.Add_Dash(app)
    
    # Global Database and Session Cache
    db = SQLAlchemy()
    redis_store = FlaskRedis()

    with app.app_context():
        # Create the Application Context
        redis_store.uri = app.config['SQLALCHEMY_DATABASE_URI']
        redis_store.endpoint = app.config['ENDPOINT']
        db = SQLAlchemy(app)

        # Construct the data set
        from . import routes
        app.register_blueprint(routes.dashboard_blueprint)

        return app
```

It's almost as though nothing changed! In fact, the only line we have regarding Dash here is `dash_app = data.Add_Dash(app)`.

We import 'dashview' at the start of ` __init.py__ `. What is this, you might ask? It's actually a file named `dashview.py` living in our application folder. Dash apps like to have a single _.py_ file per view, which turns out to work great for us. Let's look at why this works by checking out `dashview.py`:

```
import glob
from dash import Dash
import dash_table
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd

def Add_Dash(server):
    """Populates page with previews of datasets."""
    dash_app = Dash(server=server, url_base_pathname='/dataview/')
    dash_app.css.append_css({
        "external_url": "https://derp.sfo2.digitaloceanspaces.com/style.css"
        })

    # Create layout
    dash_app.layout = html.Div(
        id='flex-container'
      )

    return dash_app.server
```

We pass our Flask instance to `Add_Dash` as a parameter called _server._ Unlike the previous examples, it's actually _server_ running the show this time, with Dash piggybacking as a module. This is our most important line of code:

```
dash_app = Dash(server=server, url_base_pathname='/dataview/')

```

Dash doesn't handle routes like Flask does (or at all, really). That's fine! We start `dash_app` with URL prefix, which means the Dash logic here is confined to that single page. This means we can build a sprawling Flask app with hundreds of features and views, and oh yeah, if we want a Dash view, we can just create a file for that to chill on its own, not touching anything else.

Now you're thinking with portals **™.**

### Just for Funsies

Here's a fun little thing I did Dash in this context. In our file dashview, I have the app look at a folder of extracted datasets (called _/datasets_). For each dataset, I use Pandas and Flask's data tables to create a preview of each dataset import to our app. This lets us quickly cruise through the data an app depends on with a cool interface:

![Cracking Full Control Over Plot.ly Dash](http://res-3.cloudinary.com/hackers-and-slackers/image/upload/q_auto/v1/images/dataframesgif.gif)<figcaption>A bit rough around the edges, but you get the point.</figcaption>

Here's the source I wrote to create this:

```
import glob
from pathlib import Path, PurePath
from dash import Dash
import dash_table
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd

p = Path('.')

def Add_Dash(server):
    """Populates page with previews of datasets."""
    dash_app = Dash(server=server, url_base_pathname='/dataview/')
    dash_app.css.append_css({
        "external_url": "https://derp.sfo2.digitaloceanspaces.com/style.css"
        })

    # Create layout
    dash_app.layout = html.Div(
        id='flex-container',
        get_datasets(),
      )

    return dash_app.server

def get_datasets():
    """Gets all CSVS in datasets directory."""
    data_filepath = list(p.glob('application/datasets/*.csv'))
    arr = []
    for index, csv in enumerate(data_filepath):
        print(PurePath(csv))
        df = pd.read_csv(data_filepath[index]).head(10)
        table_preview = dash_table.DataTable(
            id='table' + str(index),
            columns=[{"name": i, "id": i} for i in df.columns],
            data=df.to_dict("rows"),
            sorting=True,
        )
        arr.append(table_preview)
    return arr
```

Needless to say, there's way more cool shit we can accomplish with Plot.ly Dash. Stick around long enough, and chances are we'll cover all of them.