📣 Dash 2.9.2 Released - Partial Property Updates with Patch(), Duplicate Outputs, dcc.Geolocation, Scatter Group Attributes and More

Thanks for welcome Adam!

What I meant is that now when I have 2 callbacks targeting same output i.e. one of them handling data and one of them handling style I have to prevent initial call at one of them because there is no way to set order in which they fire on initial load. My idea was that instead prevent initial call one would set initial order for each callback (i.e. 1 for data 2 for style) so there would be clear order in which the callbacks fire on page load. I hope it is clear what I mean from this description :slight_smile:

hi @martin2097
We actually talked about that possibility, but we decided not to move forward with that for the time being, because the solution was not really straightforward. It would require modifying the rendered in a specific way which might cause other issues.

1 Like

Thank you so much Dash team. I’m so proud of you guys.

4 Likes

Thanks for explanation. Nevertheless it is fantastic update and it will make dash life much easier!

2 Likes

I guess the gotcha associated with (n callbacks → 1 output) can be updated?

2 Likes

Very hyped about duplicate callback outputs and partially update figures!! Thanks heaps :slight_smile:

4 Likes

Good point, @rictuar . Thank you for the tip.

Patch() looks really nice! Makes data-related callbacks look a lot more neat.

1 Like

Fantastic! The multiple output will make code much cleaner.
Regarding the partial updates, how would one update only a column of data in dash datable? The data is a list of dictionaries, that does not seem obvious to me
Thanks

1 Like

hi @mc2pezwb

It depends what part of the column you’d like to update.

If, for example, you’d like to update the first cell in the first column, try to figure out how to do that within a list of dictionaries. Then, you could try to implement that with Patch().

Let’s take the Iris dataset.

import plotly.express as px
import pandas as pd

df = px.data.iris()
dff = df.to_dict("records")  # this is the data format accepted by the DataTable
dff[0].update({'sepal_length': 99.1})  # update first cell of column `sepal_length` to 99.1
print(dff)  # print to see result

Once you confirmed your result, you can try to implement the same thing with Patch().

from dash import Dash, html, Input, Output, Patch, dash_table, no_update
import plotly.express as px

app = Dash(__name__)

df = px.data.iris()
# dff = df.to_dict("records")  # this is the data format accepted by the DataTable
# dff[0].update({'sepal_length': 99.1})  # update first cell of column `sepal_length` to 99.1
# print(dff)  # print to see result
# exit()

app.layout = html.Div(
    [
        html.Button("Update first cell of first row only once", id="add-data-rows"),
        dash_table.DataTable(
            data=df.to_dict("records"),
            columns=[{"name": i, "id": i} for i in df.columns],
            page_size=10,
            id="df-table",
        ),
    ]
)


@app.callback(
    Output("df-table", "data"),
    Input("add-data-rows", "n_clicks"),
    prevent_initial_call=True,
)
def add_data_to_fig(n_clicks):

    if n_clicks == 1:
        patched_table = Patch()
        # Instead of `dff[0].update({'sepal_length': 99.1})`
        patched_table[0].update({'sepal_length': 99.1}) 
        return patched_table
    else:
        return no_update

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

1 Like

Great, ok so basically, using iloc to get the list position. This is rather neat.
Thanks a lot for the quick answer !

I just wanted to ask to clarify:
If I have a certain output that is shared by multiple callbacks, all I have to do is just add “allow_duplicate=True” to only one of them?
Does it matter which callback is given the tag?
And also does adding that tag to one of the callback sets that output to ensure duplicates are allowed for all other and future callbacks using that same output?

hi @hypervalent
if you have two callbacks that have the same output component property, only one of them needs an allow_duplicate=True with prevent_initial_call=True.

But if you add more callbacks with the same output component property, you will need to add the allow_duplicate=True and the prevent_initial_call=True to those callbacks as well.

See how in the example below the first and third callbacks have allow_duplicate=True. Try to remove that from the third callback and you will get an error.

from dash import Dash, Input, Output, html, dcc, Patch
import plotly.express as px
import plotly.graph_objects as go

app = Dash(__name__)

app.layout = html.Div([
    html.Button('Draw Graph', id='draw-2'),
    html.Button('Reset Graph', id='reset-2'),
    html.Button('Change Title', id='title-2'),
    dcc.Graph(id='duplicate-output-graph')
])

@app.callback(
    Output('duplicate-output-graph', 'figure', allow_duplicate=True),
    Input('draw-2', 'n_clicks'),
    prevent_initial_call=True
)
def draw_graph(n_clicks):
    df = px.data.iris()
    return px.scatter(df, x=df.columns[0], y=df.columns[1])

@app.callback(
    Output('duplicate-output-graph', 'figure'),
    Input('reset-2', 'n_clicks'),
)
def reset_graph(input):
    return go.Figure()

@app.callback(
    Output('duplicate-output-graph', 'figure', allow_duplicate=True),
    Input('title-2', 'n_clicks'),
    prevent_initial_call=True
)
def draw_graph(n_clicks):
    patched_figure = Patch()
    if n_clicks % 2 != 0:
        patched_figure['layout']['title'] = 'Title 1'
    else:
        patched_figure['layout']['title'] = 'Title 2'

    return patched_figure



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

1 Like

Hello @adamschroeder ! Thank you for the quick response!

Just to make sure then, in the case of, say, n callbacks with the same output. All callbacks except one of them (n-1 callbacks) require the allow_duplicate=True ?

And in the case of there being more than 2 callbacks with the same output, does it matter which callback does not get the allow_duplicate=True ? I know in your specific scenario it’s because the second callback does not want prevent_initial_call=True, but in the case where all of the callbacks need prevent_initial_call=True, does it ultimately matter which one I remove the allow_duplicate tag?

Thank you!

There are so many cool new features in 2.9.2, I think this is my favorite release to date. :sparkler:

I just wanted to highlight a few posts with more great info on the new things now available.
(And be sure to update to 2.9.3 to get the patch with the minor bugs reported in 2.9.2)

Examples of multi-page app navigation without refreshing the page

Anyone making multi-page apps where you update the URL in a callback will :heart: this new feature! Super fast navigation without the annoying page refresh. See more information and examples in this post:

callback-nav




Geolocation :earth_americas:

When deploying an app with the the new dcc.Geolocation be sure to be using HTTPS, else Geolocation will not be enabled.

Here is another demo app showing more features available. This one also uses geopy to get the address (if the “Include Address” checkbox is selected). Try it out on a phone or some other device with GPS enabled. The accuracy is amazing

https://dccgeolocation.pythonanywhere.com/




Examples of partial update and allow duplicates

This post has some great examples from community members @AIMPED and @jinnyzor including examples with pattern matching callbacks.

4 Likes

hi @hypervalent ,

Just to make sure then, in the case of, say, n callbacks with the same output. All callbacks except one of them (n-1 callbacks) require the allow_duplicate=True ?

That is correct. They will also need the prevent_initial_call=True. Same output meaning the same component_id as well as the same component_property used.

Regarding your second question,

Does it matter which callback does not get the allow_duplicate=True ?

I don’t think it matters that much, or at least I haven’t encountered a case where I it made a difference. If you encounter that case, let me know.

1 Like

Thank you so much! That pretty much answers all my questions

I will let you know if I encounter such a case

A post was split to a new topic: Label Aliases wot working, plotly 5.14.1

This is an amazing release! So many great features :heart_eyes: :tada:

1 Like

I see this is not fixed in 2.9.3, should we reopen a bug report?