Populate a dictionary using dcc.Store

Hi comunity,

I’m currently facing a challenging but probably easy to solve problem. I have read many threads, tested many different codes, but I simply can’t solve my issue.

Context : I am writing a survey in python dash and I would like to record the answer. As the survey is quite long I use a multi pages approach (I provide a dummy code here).

the app.py looks like this:

import dash
  from dash import dcc, html
  import dash_bootstrap_components as dbc
  import pandas as pd
  
  app = dash.Dash(__name__, use_pages=True)
  
  app.prevent_initial_callbacks='initial_duplicate'
  
  app.layout = html.Div([
      dbc.Container(
      [dash.page_container],
      fluid=True
      ),
      dcc.Store(id='record_answers', data= {}}, storage_type='local')
  ])
  
  if __name__ == "__main__":
      app.run_server(debug=True)

The page 1 code is:

import dash
  
  dash.register_page(__name__, path='/')
  
  from dash import Dash, dcc, html, Input, Output, callback, State
  
  layout = html.Div([
      html.H1('Question 1'),
      html.Label(children = 'How many unicorns have you seen in the past 5 years ?'),
      html.Br(),
      dcc.RadioItems(options=['0','1', '2', '3'], value = '0',id='1'),
      html.Br(),
      html.H1('Question 2'),
      html.Label(children = 'How many vampires have you seen in the past 5 years ?'),
      html.Br(),
      dcc.RadioItems(options=['0','1', '2', '3'], value = '0', id='2'),
      html.Br(),
      dcc.Link('Next page', href='/page-2'),
      html.H1(children='', id='toprint')
  ], style={'border': '2px solid black', 'padding': '20px'}
  )
  
  @callback(
      Output(component_id='toprint', component_property='children'),
      [Input(component_id='record_answers', component_property='data'),
      Input(component_id='1', component_property='value'),
      Input(component_id='2', component_property='value')],
      prevent_initial_call=True
  )
  
  def update_dic(data, Q1, Q2):
      data['Q1'] = Q1
      data['Q2'] = Q2
      return str(data)

Page 2 code is:

import dash

dash.register_page(__name__, path='/page-2')

from dash import Dash, dcc, html, Input, Output, callback, State

layout = html.Div([
    html.H1('Question 3'),
    html.Label(children = 'How many ETs have you seen in the past 5 years ?'),
    html.Br(),
    dcc.RadioItems(options=['0','1', '2', '3'], value = '0', id='3'),
    html.Br(),
    html.H1('Question 4'),
    html.Label(children = 'How many Yetis have you seen in the past 5 years ?'),
    html.Br(),
    dcc.RadioItems(options=['0','1', '2', '3'], value = '0', id='4'),
    html.Br(),
    #dcc.Link('Next page', href='/page-3'),
    html.H1(children='', id='toprint2')
], style={'border': '2px solid black', 'padding': '20px'}
)

@callback(
    Output(component_id='toprint2', component_property='children'),
    [Input(component_id='record_answers', component_property='data'),
    Input(component_id='3', component_property='value'),
    Input(component_id='4', component_property='value')],
    prevent_initial_call=True
)

def update_dic(data, Q3, Q4):
    data['Q3'] = Q3
    data['Q4'] = Q4
    return str(data)

Problem : I know this code is dummy but I provide it as a basis for further corrections. I have tested all the memory types in the dcc.Store(). I also tried to provide the dcc.Store data as Ouput in the different callbacks but nothing works properly.

In this specific case, I would like to get a final dictionary like : {‘Q1’ : 0, ‘Q2’ : 1, ‘Q3’ : 3, ‘Q4’ :, 0} at the end of page2.

Could you give me some help ? Thanks in advance !

Well, you are never saving the data to the store only attempting to retrieve it. Setup the callbacks with an output of ‘record_answers’ send the dictionary as a return to that store then create a new callback to retrieve it from input. So for example:

@callback(
    Output(component_id='record_answers', component_property='data'),
    Input(component_id='3', component_property='value'),
    Input(component_id='4', component_property='value'),
    prevent_initial_call=True
)

def update_dic(data, Q3, Q4):
    data['Q3'] = Q3
    data['Q4'] = Q4
    return str(data)

then create another callback to retrieve it:

@callback(
    Output(component_id='toprint2', component_property='children'),
    Input(component_id='record_answers', component_property='data'),
)

def return_dic(data):
   if data:
      return str(data)
1 Like

Thanks for your help. At first glance, I’ll have a a pb with the def update_dic callback as I have only two arguments in the callback and 3 in the update_dic function ?

Yeah, basically the problem with your code is you are only using Input for your dcc.Store. The Input takes the value of an object. Your dcc.Store has a value of None. You need to set the value with an Output in your callback refering to dcc.Store then use the input to retrieve the data after you’ve saved the data to the dcc.Store

.I know. As I said in the first message, I tested this option. It works but I get plenty of error messages concerning the Inputs:

app.py:

import dash
from dash import dcc, html
import dash_bootstrap_components as dbc
import pandas as pd

app = dash.Dash(__name__, use_pages=True)

app.prevent_initial_callbacks='initial_duplicate'

app.layout = html.Div([
    dbc.Container(
    [dash.page_container],
    fluid=True
    ),
    dcc.Store(id='record_answers', data= {}, storage_type='local')
])

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

Page 1:

import dash

dash.register_page(__name__, path='/')

from dash import Dash, dcc, html, Input, Output, callback, State

layout = html.Div([
    html.H1('Question 1'),
    html.Label(children = 'How many unicorns have you seen in the past 5 years ?'),
    html.Br(),
    dcc.RadioItems(options=['0','1', '2', '3'], value = '0',id='1'),
    html.Br(),
    html.H1('Question 2'),
    html.Label(children = 'How many vampires have you seen in the past 5 years ?'),
    html.Br(),
    dcc.RadioItems(options=['0','1', '2', '3'], value = '0', id='2'),
    html.Br(),
    dcc.Link('Next page', href='/page-2'),
    html.H1(children='', id='toprint')
], style={'border': '2px solid black', 'padding': '20px'}
)

@callback(
    Output(component_id='record_answers', component_property='data', allow_duplicate=True),
    [Input(component_id='record_answers', component_property='data'),
    Input(component_id='1', component_property='value'),
    Input(component_id='2', component_property='value')],
    prevent_initial_call=True
)

def update_dic(data, Q1, Q2):
    data['Q1'] = Q1
    data['Q2'] = Q2
    return data

@callback(
    Output(component_id='toprint', component_property='children'),
    Input(component_id='record_answers', component_property='data'),
)

def return_dic(data):
   if data:
      return str(data)

Page 2:

#!/usr/bin/env python3

# Import packages
import dash

dash.register_page(__name__, path='/page-2')

from dash import Dash, dcc, html, Input, Output, callback, State

layout = html.Div([
    html.H1('Question 3'),
    html.Label(children = 'How many ETs have you seen in the past 5 years ?'),
    html.Br(),
    dcc.RadioItems(options=['0','1', '2', '3'], value = '0', id='3'),
    html.Br(),
    html.H1('Question 4'),
    html.Label(children = 'How many Yetis have you seen in the past 5 years ?'),
    html.Br(),
    dcc.RadioItems(options=['0','1', '2', '3'], value = '0', id='4'),
    html.Br(),
    #dcc.Link('Next page', href='/page-3'),
    html.H1(children='', id='toprint2')
], style={'border': '2px solid black', 'padding': '20px'}
)

@callback(
    Output(component_id='record_answers', component_property='data', allow_duplicate=True),
    [Input(component_id='record_answers', component_property='data'),
    Input(component_id='3', component_property='value'),
    Input(component_id='4', component_property='value')],
    prevent_initial_call=True
)

def update_dic(data, Q3, Q4):
    data['Q3'] = Q3
    data['Q4'] = Q4
    return data

@callback(
    Output(component_id='toprint2', component_property='children'),
    Input(component_id='record_answers', component_property='data'),
    prevent_initial_call=True
)

def return_dic(data):
   if data:
      return str(data)

Doing so, I am able to update my dictionary but I get this kind of error messages:

app.py:

import dash
from dash import dcc, html
import dash_bootstrap_components as dbc
import pandas as pd

app = dash.Dash(__name__, use_pages=True)

app.prevent_initial_callbacks='initial_duplicate'

app.layout = html.Div([
    dbc.Container(
    [dash.page_container],
    fluid=True
    ),
    dcc.Store(id='record_answers', data= {}, storage_type='local')
])

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

page1

import dash

dash.register_page(__name__, path='/')

from dash import Dash, dcc, html, Input, Output, callback, State

layout = html.Div([
    html.H1('Question 1'),
    html.Label(children = 'How many unicorns have you seen in the past 5 years ?'),
    html.Br(),
    dcc.RadioItems(options=['0','1', '2', '3'], value='0', id='1'),
    html.Br(),
    html.H1('Question 2'),
    html.Label(children = 'How many vampires have you seen in the past 5 years ?'),
    html.Br(),
    dcc.RadioItems(options=['0','1', '2', '3'], value='0', id='2'),
    html.Br(),
    dcc.Link('Next page', href='/page-2'),
    html.H1(children='', id='toprint')
], style={'border': '2px solid black', 'padding': '20px'}
)


@callback(
    Output('record_answers', 'data',  allow_duplicate=True),
    Output(component_id='toprint', component_property='children'),
    Input('1', 'value'),
    Input('2', 'value'),
    State('record_answers', 'data'),
    prevent_initial_call=True,
)
def update_dic(Q1, Q2, data):
    # Update the dictionary based on the input values
    data = data or {}  # Ensure data is a dictionary
    data['Q1'] = Q1
    data['Q2'] = Q2
    return data, str(data)

page2

#!/usr/bin/env python3

import dash
from dash import dcc, html, Input, Output, callback, State

# Register the page
dash.register_page(__name__, path='/page-2')

layout = html.Div([
    html.H1('Question 3'),
    html.Label('How many ETs have you seen in the past 5 years?'),
    html.Br(),
    dcc.RadioItems(options=[{'label': i, 'value': str(i)} for i in range(4)], value='0', id='3'),
    html.Br(),
    html.H1('Question 4'),
    html.Label('How many Yetis have you seen in the past 5 years?'),
    html.Br(),
    dcc.RadioItems(options=[{'label': i, 'value': str(i)} for i in range(4)], value='0', id='4'),
    html.Br(),
    # Uncomment and update href for the next page link
    # dcc.Link('Next page', href='/page-3'),
    html.H1(children='', id='toprint2')
], style={'border': '2px solid black', 'padding': '20px'}
)

# Update the global store with answers from current page
@callback(
    Output('record_answers', 'data',  allow_duplicate=True),
    [Input('3', 'value'),
     Input('4', 'value')],
    State('record_answers', 'data'),
    prevent_initial_call=True,
)
def update_dic(Q3, Q4, data):
    # Make sure to not overwrite other answers
    updated_data = data.copy() if data else {}
    updated_data['Q3'] = Q3
    updated_data['Q4'] = Q4
    return updated_data

# Display the current state of the store (for debugging or display)
@callback(
    Output('toprint2', 'children'),
    [Input('record_answers', 'data')]
)
def return_dic(data):
   return str(data) if data else "No data yet"

Cheers :clinking_glasses:

1 Like

Works like a charm ! Thank you SO MUCH !

So @PipInstallPython If I understand well, the dcc.Store data should be used as a State and Output through the different callbacks ?