General guidelines
Callbacks mechanism
Callbacks are functions that are automatically called by Dash whenever an input component's property changes. This is basically how we create a dynamic application from user inputs.
A callback is written in its general form as follow:
@app.callback(
Output(component_id='my-output', component_property='children'),
Input(component_id='my-input', component_property='value')
)
def update_output_div(input_value):
return 'Output: {}'.format(input_value)
This callback can be written in a more simple form by omitting the arguments like this:
@app.callback(
Output('my-output', 'children'),
Input('my-input', 'value')
)
def update_output_div(input_value):
return 'Output: {}'.format(input_value)
value property fo the my-input element is changed and will update the children property of my-output element.
Note
The name of function arguments can be completely arbitrary. Only the order of Input and State arguments matters.
Prevent an update
Sometimes you do not want to update an Output when a callback fires based on a logic you implement within the function. In that case, Dash provides the PreventUpdate exception you can raise to prevent an update:
@app.callback(
Output('my-output', 'children'),
Input('my-input', 'value')
)
def update_output_div(input_value):
if input_value == None:
raise PreventUpdate
return 'Output: {}'.format(input_value)
Another option is to use dash.no_update directly as return value to prevent only a specific Output to update:
@app.callback(
Output('my-output1', 'children'),
Output('my-output2', 'value'),
Input('my-input', 'value')
)
def update_output_div(input_value):
if input_value == None:
return 1, dash.no_update
return None, None
Note
You may sometimes see code with the Output and Input in brackets like this:
@app.callback(
[Output('my-output1', 'children'),
Output('my-output2', 'children')],
[Input('my-input1', 'value'),
Input('my-input2', 'value')]
)
def func(input1, input2):
return None, None
Input or Output.
However you can still create multiples Input or Output in a row by using python list comprehension, and thus, using brackets in the callback arguments in this case is relevant.
@app.callback(
[Output(f"my-output-{n}", 'children') for n in range(5)],
Input('my-input', 'value')
)
def func(input):
return None, None
Another option for the callback arguments are the State input. This latter acts as input but does not fire the callback when the value of the its property change. Thus, use the State input whenever you need an "indirect" information inside your function that must not fire the callback when its value changes.
@app.callback(
Output('my-output', 'children'),
Input('my-input', 'value'),
State('my-state', 'data')
)
def update_output_div(input_value, state_value):
return 'Output: {}'.format(state_value)
Use Dash extensions
The dash-extensions package brings various extensions to the Plotly Dash framework and should be used every times.
To add the enrichments to the app, instead to create the Dash app like this:
app = dash.Dash()
Write it like this:
from dash_extensions.enrich import DashProxy, TriggerTransform, GroupTransform, \
ServersideOutputTransform, NoOutputTransform
app = DashProxy(transforms=[
TriggerTransform(), # enable use of Trigger objects
GroupTransform(), # enable use of the group keyword
ServersideOutputTransform(), # enable use of ServersideOutput objects
NoOutputTransform(), # enable callbacks without output
])
TriggerTransform
With TriggerTransform, instead to write this:
@app.callback(Output("output_id", "output_prop"), Input("button", "n_clicks"))
def func(n_clicks):
You write that:
@app.callback(Output("output_id", "output_prop"), Trigger("button", "n_clicks"))
def func(): # note that "n_clicks" is not included as an argument
Important
Note that the Trigger input does not work when we mix it with Input and State. Then, a Trigger is only useful for very simple callback.
NoOutputTransform
This seems nothing, but the NoOutputTransform will save you to write dummy Output when you don't really need to update an output with your callback. Since originally in Dash, a callback must have at least one Output and one Input.
You will write cleaner code with this enrichment.
@app.callback(Trigger("button", "n_clicks")) # note that the callback has no output
GroupTransform
One of the biggest pain with Dash is that we cannot use the same Output in multiples callbacks, which force us to write sometimes very large callback function. Thanks to the GroupTransform enrichment, we CAN use the same Output in multiples callbacks:
@app.callback(Output("log", "children"), Trigger("left", "n_clicks"), group="my_group")
def left():
return "left"
@app.callback(Output("log", "children"), Trigger("right", "n_clicks"), group="my_group")
def right():
return "right"
Important
This new feature will bundle callbacks together, thus to make it work properly, the number of outputs of the callback functions must be the same.
ServersideOutputTransform
This new output works like a normal Output but it keeps the data on the server, reducing the network overhead. This is particularly useful when we work with more complex objects such as a pandas DataFrame and we can return the DataFrame directly without having to serialize this latter to JSON.
@app.callback(ServersideOutput("store", "data"), Trigger("left", "n_clicks"))
def query():
return pd.DataFrame(data=list(range(10)), columns=["value"])
Use the cache
We can use the cache and memoize the results of a callback function. Memoization stores the results of a function after it is called and re-uses the result if the function is called with the same arguments.
The cache is not directly related to Dash, but Flask. Thus, we have to install the package flask_caching. Once installed, we can enable the cache with this simple code:
from flask_caching import Cache
...
CACHE_CONFIG = {
'CACHE_TYPE': 'filesystem',
'CACHE_DIR': 'cache-directory',
}
cache = Cache()
cache.init_app(app.server, config=CACHE_CONFIG)
with app.server.app_context():
cache.clear()
For more information about Flask caching configuration, consult the package documentation.
In the case above we tell Flask to store the cache in a local folder called cache-directory.
Then, to memoize the result of a callback we use the decorator @cache.memoize() as follow:
@app.callback(
Output('flask-cache-memoized-children', 'children'),
Input('flask-cache-memoized-dropdown', 'value'))
@cache.memoize()
def render(value):
return 'Selected "{}" at "{}"'.format(
value, datetime.datetime.now().strftime('%H:%M:%S')
)
For more information about the performance of the application, consult the dedicated official Performance page.
Dash deployment
When we invoke app.run_server(), behind Dash creates a Flask server instance. We can pass Flask server arguments directly to that function if we want to change some configuration of the Flask server.
In a standard deployment of a Dash application, on a UNIX platform, it is recommended to use gunicorn which is a python WSGI HTTP Server for Unix.
To deploy with gunicorn:
pip install gunicorn
gunicorn -b 0.0.0.0:8050 index:app.server
index.py and that the file contain the app instance.
On a Windows platform, one can deploy a Dash application with waitress but it requires you to write little extra code in the index.py:
from waitress import serve
serve(app.server, host='0.0.0.0', port=8050)