GibbsConsulting / django-plotly-dash

Expose plotly dash apps as django tags
MIT License
538 stars 121 forks source link

How to pass multiple pandas Time Series (created in Django view) to Dash callback? #479

Open GarderesG opened 7 months ago

GarderesG commented 7 months ago

I have a Django app aiming to track my stock portfolio daily value (Time Series 1) and daily return (Time Series 2). These 2 Time Series are computed in Django based on custom models and methods (ts_value and ts_ret).

views.py

def home(request):
    ...
    context = {
        "dash_context": {"ts-value": ts_value, "ts-ret": ts_ret }
    } 
    return render(request, "home.html", context)

On my homepage, I want to display these Time Series in a Dash app with 2 buttons: "Returns" and "Prices". Depending on the button pressed, only one of the 2 time series will be plotted.

dash_app.py

import dash
from dash import dcc, html

from django_plotly_dash import DjangoDash

app = DjangoDash('Dashboard')
app.layout = html.Div(children=[
    # Price / Return mode
    dcc.RadioItems(
        id="radio-chart-mode", 
        options=["Prices", "Returns"],
        value="Returns",
        inline=True),

    # Buttons below for dates
    html.Div([
        html.Button(id='btn-horizon-1m', children="1m"),
        html.Button(id='btn-horizon-3m', children="3m"),
        html.Button(id='btn-horizon-6m', children="6m"),
        html.Button(id='btn-horizon-ytd', children="YTD"),
        html.Button(id='btn-horizon-1y', children="1Y"),
        html.Button(id='btn-horizon-3y', children="3Y"),
        html.Button(id='btn-horizon-max', children="Max")
        ]),

    # The Time Series chart
    html.Div(dcc.Graph(id='graph-ts', figure={})),
])

# Price/Return callback
@app.callback(
    dash.dependencies.Output('graph-ts', 'figure'),
    [dash.dependencies.Input('radio-chart-mode', 'value')],
    prevent_initial_call=False
)
def update_the_graph(chart_mode: str):
    if (chart_mode == "Prices"):
        # Draw Prices Graph based on ts_value
        pass

    elif chart_mode == "Returns":
        # Draw Return Graph based on ts_ret 
        pass

My issue is that I can't pass the time series objects (not even one of the 2) through initial_argument parameter in the HTML template of my homepage (I also tried with ts_value.to_json() or directly a Graph object instead of ts_value in the context, but was not successful).

home.html

{%load plotly_dash%}
{%plotly_app name="Dashboard" initial_arguments=dash_context ratio=1%}

What could be the best way to do this?

delsim commented 7 months ago

@GarderesG this is not an uncommon issue - in general passing the name (or some other label/tag) of the data to the Dash app through the template, and then using standard Django functionality to access the data inside the callback, is a common use case IMHO.

This means that the real question is what is the best way of making that data available. This depends a lot on your application but in general putting something into a cache using other Django views (eg the one with the template in is usually good; refreshing the app page then repopulates the cache) or mechanism (eg a timer with celery) seems to work well.

You probably don't want to do too much work during a callback, but at the same time if the data is relatively cheap you could have the 'get from cache or repopulate it' logic inside the callback if external population is a step too far.

GarderesG commented 7 months ago

Thank you for your reply @delsim. Could you point me in the right direction to implement the 'get from cache or repopulate it' logic? I do not have a huge experience with Django, and I have had trouble finding relevant information about it. Would love to get more details :)