Black Lives Matter. Please consider donating to Black Girls Code today.

Pass a value through two consequent callbacks

Hello, everyone!

Here’s the deal: I have a table and 2 controls (DatePickerSingle and RangeSlider), which puts two different filters on this table. I’d like to implement this scenario:

  1. User selects a date -> talbe gets filtered by this date
  2. User selects an hours range -> table gets filtered by date and hours

I made a chain of callbacks: DatePicker -> RangeSlider -> Table, but there’s a problem: when I fire Table’s callback, I only know hours range, but I lose the date. DatePicker’s value simply can’t pass through Slider’s callback.

So, the question is: is there a way to somehow pass a value through two consequent callbacks?

BTW, here’s the code. I use Redis as cache for long-calculated DataFrame. I need to filter “Schedule” table by “day” and “hour” controls

import os
import re

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table_experiments as dt
from dash.dependencies import Input, Output, State
from flask_caching import Cache

import plotly

import pandas as pa
import engine


app = dash.Dash()

CACHE_CONFIG = {
    # try 'filesystem' if you don't want to setup redis
    'CACHE_TYPE': 'redis',
    'CACHE_REDIS_URL': os.environ.get('REDIS_URL', 'localhost:6379')
}
cache = Cache()
cache.init_app(app.server, config=CACHE_CONFIG)

@cache.memoize()
def get_pd(set_name):
    data, mess = engine.update()
    if set_name == 'data':
        return data
    elif set_name == 'mess':
        return mess
    else:
        return [] 



d = get_pd('data')
d = d[['Date']]
d = d.drop_duplicates().sort_values(by = 'Date')
days = d['Date'].tolist()

print(days[0])
print(days[-1])


app.layout = html.Div([
    html.H1('Schedule'),

    html.Br(),
    html.Br(),

    dcc.DatePickerSingle(
        date = days[0],
        min_date_allowed = days[0],
        max_date_allowed = days[-1],
        display_format = 'DD.MM.YY',
        id = 'day'
    ),

    html.Br(),
    html.Br(),

    dcc.RangeSlider(
        min = 8,
        max=20,
        marks = {i: '{}'.format(i) for i in range(8,21)},
        value = [i for i in range(10,13) ],
        allowCross = False,
        count = 0,
        id='hour'
    ),
   
    html.Br(),
    html.Br(),

    html.Button('Refresh', id='btn_update'),
    dt.DataTable(
        rows=[{}],
        row_selectable=True,
        filterable=True,
        sortable=True,
        selected_row_indices=[],
        id='schedule'
    ),
    html.H3('Unrecognized:'),

    dt.DataTable(
        rows=[{}],
        row_selectable=True,
        filterable=True,
        sortable=True,
        selected_row_indices=[],
        id='mess'
        ),
    html.H3('')
    ]) #end of layout




@app.callback(
    Output('day','date'),
    [Input('btn_update','n_clicks')]
)
def update_schedule(n_clicks):
    data = get_pd('data')
    
    return format_date_for_picker(data.agg({'Date':'min'}))

@app.callback(
    Output('hour','value'),
    [Input('day','date')]
)
def filter_by_day(date):
    data = get_pd('data')
    d = data[data['Date'].isin(value)]
    d = d[['Date', 'Name','Description']]


    return {i: ''.format(i) for i in range(8,21) } 



@app.callback(
    Output('schedule','rows'),
    [Input('hour','value')]
)
def filter_by_hour(value):
    data = get_pd('data')
    d = data[data['Hour'].isin(value)]
    d = d[['Date', 'Name','Description']]

    return d.to_dict('rows')

@app.callback(
    Output('mess','rows'),
    [Input('schedule','rows'),
    Input('schedule', 'selected_row_indices')]
)
def update_mess(rows, selected_row_indices):
    mess = get_pd('mess')
    return mess.to_dict('records')

Why don’t you just have one callback that takes the input from both the datepicker and rangeslider and filters the datatable? Throw an if statement in there to check whether to filter by date or by date + hour and bob’s your uncle (unless I’m missing something).

1 Like

I dont understand some places
1)
def filter_by_day(date):
data = get_pd(‘data’)
d = data[data[‘Date’].isin(value)] #what is value variable in scope of that def? is it date from def filter_by_day(date)?

  1. if you want to filter your schedule table with date and hour then why it isn’t subscribed to datepicker?

@app.callback(
Output(‘schedule’,‘rows’),
[Input(‘hour’,‘value’),
Input(‘day’,‘date’]
)

  1. i’m not sure about is it good to update hours control and table at same time. mb its ok i just havent try to.
1 Like
  1. it’s just a bad copy-paste
  2. That’s it! I just missed such an option
  3. I tried ti reset Hours control (and make a chain of callbacks). This really may be a bad decision, I’ll think about it.

Anyways, thanks you both for a solution! I really missed an option to have many sources for callback. That’s what I needed.

1 Like