================== General guidelines ================== .. contents:: :local: :depth: 1 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: .. code-block:: python @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: .. code-block:: python @app.callback( Output('my-output', 'children'), Input('my-input', 'value') ) def update_output_div(input_value): return 'Output: {}'.format(input_value) This callback will fire whenever the ``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: .. code-block:: python @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: .. code-block:: python @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: .. code-block:: python @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 This is no more necessary since an update of Dash and the brackets can be omitted even with multiples ``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. .. code-block:: python @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. .. code-block:: python @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: .. code-block:: python app = dash.Dash() Write it like this: .. code-block:: python 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: .. code-block:: python @app.callback(Output("output_id", "output_prop"), Input("button", "n_clicks")) def func(n_clicks): You write that: .. code-block:: python @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. .. code-block:: python @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: .. code-block:: python @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. .. code-block:: python @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: .. code-block:: python 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: .. code-block:: python @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``: .. prompt:: bash pip install gunicorn gunicorn -b 0.0.0.0:8050 index:app.server Assuming that the main entry of your application is ``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``: .. code-block:: python from waitress import serve serve(app.server, host='0.0.0.0', port=8050) :tag:`Python` :tag:`Plotly Dash`