# Dynamic Filter in Dash python

**URL:** <https://community.plotly.com/t/dynamic-filter-in-dash-python/69679>\
**Category:** Dash Python\
**Tags:** question\
**Created:** [November 8, 2022, 2:13am UTC](https://community.plotly.com/t/dynamic-filter-in-dash-python/69679 "2022-11-08T02:13:55Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![phuonghao145](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/phuonghao145/32/15805_2.png) [@phuonghao145](https://community.plotly.com/u/phuonghao145)\
**Post date:** [November 8, 2022, 2:13am UTC](https://community.plotly.com/t/dynamic-filter-in-dash-python/69679/1 "2022-11-08T02:13:55Z")

</div>

Hi all.  
I would like to create a dynamic filter as the redmine page here:

 ![image](https://us1.discourse-cdn.com/flex024/uploads/plot/original/3X/f/a/fad4055d64edc6494f526ea05a2e87620fbf46b9.png)  
It will allows users can custom the filter. When user click to add filter button, it will show list name column in data that we want to filter.  
Are there any ways to do it ?  
Please help me.  
Thanks

---

<div class="post-metadata">

**Author:** ![hoatran](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/hoatran/32/21447_2.png) [@hoatran](https://community.plotly.com/u/hoatran)\
**Post date:** [November 8, 2022, 2:49am UTC](https://community.plotly.com/t/dynamic-filter-in-dash-python/69679/2 "2022-11-08T02:49:05Z")

</div>

So basically you need a main dropdown with options in it and then you use it to return other dropdown. Below is my sample code. Hope this help:

```auto
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc

app = dash.Dash( __name__ ,external_stylesheets=[dbc.themes.LUX])

app.layout = html.Div([
    dbc.Row([
        dbc.Col([
            html.H6('Add filter'),
            dcc.Dropdown(id='main_dropdown',
                         options=[
                             {'label': 'New York City', 'value': 'New York City'},
                             {'label': 'Montreal', 'value': 'Montreal'},
                             {'label': 'San Francisco', 'value': 'San Francisco'},
                         ],
                         value=[])
        ],width={'size':3,'offset':0,'order':1}),
        dbc.Col([
            html.Div(id='sub_dropdown_1')
        ],width={'size':3,'offset':0,'order':1})
    ], className='p-2 align-items-stretch')
])

@app.callback(Output('sub_dropdown_1', 'children'),
             [Input('main_dropdown', 'value')])

def update_options_1(main_dropdown):
    if main_dropdown == 'New York City':
        return html.H6('Filters'), dcc.Dropdown(
                         options=[
                             {'label': 'Harlem', 'value': 'Harlem'},
                             {'label': 'Chinatown', 'value': 'Chinatown'},
                             {'label': 'Flushing', 'value': 'Flushing'},
                         ],
                         value='Harlem')
    elif main_dropdown == 'Montreal':
        return html.H6('Filters'), dcc.Dropdown(
                         options=[
                             {'label': 'Old Montreal', 'value': 'Old Montreal'},
                             {'label': 'Downtown Montreal', 'value': 'Downtown Montreal'},
                             {'label': 'Mile End', 'value': 'Mile End'},
                         ],
                         value='Old Montreal')

    elif main_dropdown == 'San Francisco':
        return html.H6('Filters'), dcc.Dropdown(
                         options=[
                             {'label': 'Financial District', 'value': 'Financial District'},
                             {'label': 'Mission District', 'value': 'Mission District'},
                             {'label': 'Japan Town', 'value': 'Japan Town'},
                         ],
                         value='Japan Town')
    else:
        return html.Div()
    
    
if __name__ == " __main__":
    app.run_server(debug=False,port=1115)

```

P/s: Are you Vietnamese?

---

<div class="post-metadata">

**Author:** ![phuonghao145](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/phuonghao145/32/15805_2.png) [@phuonghao145](https://community.plotly.com/u/phuonghao145)\
**Post date:** [November 8, 2022, 3:51am UTC](https://community.plotly.com/t/dynamic-filter-in-dash-python/69679/3 "2022-11-08T03:51:55Z")

</div>

Hi Hoa, I’m Vietnamese. Thanks for your solution , but I want to create multi filter It means when I click add filter, it will create new filter as here: [Issues - Redmine](https://www.redmine.org/projects/redmine/issues)

---

<div class="post-metadata">

**Author:** ![jinnyzor](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/jinnyzor/32/21194_2.png) [@jinnyzor](https://community.plotly.com/u/jinnyzor)\
**Post date:** [November 8, 2022, 4:03am UTC](https://community.plotly.com/t/dynamic-filter-in-dash-python/69679/4 "2022-11-08T04:03:54Z")

</div>

Sure, @phuonghao145,

It looks like you could benefit from pattern-matching callbacks, check it out:

> **[Pattern-Matching Callbacks | Dash for Python Documentation | Plotly](https://dash.plotly.com/pattern-matching-callbacks)**
>
> The pattern-matching callback selectors \`MATCH\`, \`ALL\`, & \`ALLSMALLER\` allow you to write callbacks that respond to or update an arbitrary or dynamic number of components. New in Dash 1.11.0!

With this, you can add dynamic amounts of elements that all can target the same output, without having to customize your code too much.

---

<div class="post-metadata">

**Author:** ![hoatran](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/hoatran/32/21447_2.png) [@hoatran](https://community.plotly.com/u/hoatran)\
**Post date:** [November 8, 2022, 9:27am UTC](https://community.plotly.com/t/dynamic-filter-in-dash-python/69679/5 "2022-11-08T09:27:28Z")

</div>

@jinnyzor: I see that her requirement is more complicated that after choosing value from first filter, it will return another filter based on it. So I did something as below but I didn’t figure out how to disable options after choosing and keep the Checkbox as her example. Can you give me a look and a suggestion. Or can you give us a sample with pattern matching. Thank you.

```auto
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc

app = dash.Dash( __name__ ,external_stylesheets=[dbc.themes.LUX])

app.layout = html.Div([
    dbc.Row([
        dbc.Col([
            dcc.Dropdown(id='main_dropdown',
                         options=[
                             {'label': 'Status', 'value': 'Status'},
                             {'label': 'Tracker', 'value': 'Tracker'},
                             {'label': 'Priority', 'value': 'Priority'},
                         ],
                         value='Status',
                        disabled=False)
        ],width={'size':3,'offset':0,'order':1}),
        dbc.Col([
            dbc.Checkbox(label="Status",
            value=True,
            id="checklist-input",
        )
        ],width={'size':1,'offset':0,'order':1}),
        dbc.Col([
            html.Div(id='sub_dropdown_1')
        ],width={'size':3,'offset':0,'order':1})
    ], className='p-2 align-items-center'),
    
    dbc.Row([
        dbc.Col([
            html.Div(id='checklist-input-2')
        ],width={'size':1,'offset':3,'order':1}),
        dbc.Col([
            html.Div(id='sub_dropdown_2')
        ],width={'size':3,'offset':0,'order':1})        
    ], className='p-2 align-items-center'),
    dbc.Row([
        dbc.Col([
            html.Div(id='checklist-input-3')
        ],width={'size':1,'offset':3,'order':1}),
        dbc.Col([
            html.Div(id='sub_dropdown_3')
        ],width={'size':3,'offset':0,'order':1})        
    ], className='p-2 align-items-center'),
])

@app.callback(Output('sub_dropdown_1', 'children'),
             [Input('checklist-input', 'value')])

def update_options_1(main_dropdown):
    if main_dropdown == True:
        return html.Div(
            dbc.Row([
            dcc.Dropdown(
                options=[
                    {'label': 'New', 'value': 'New'},
                    {'label': 'Confirmed', 'value': 'Confirmed'},
                    {'label': 'Closed', 'value': 'Closed'},
                ],
                value='New')
            ])
        )
    else:
        return html.Div()
    
    
@app.callback(Output('checklist-input-2', 'children'),
             [Input('main_dropdown', 'value')])

def update_options_1(main_dropdown):
    if main_dropdown == 'Tracker':
        return dbc.Checkbox(label="Tracker",
            value=True,
            id="checklist-input-2-2",
        )
    else:
        return html.Div()    

@app.callback(Output('sub_dropdown_2', 'children'),
             [Input('checklist-input-2-2', 'value')])

def update_options_1(main_dropdown):
    if main_dropdown == True:
        return html.Div(
            dbc.Row([
            dcc.Dropdown(
                options=[
                    {'label': 'Defect', 'value': 'Defect'},
                    {'label': 'Feature', 'value': 'Feature'},
                    {'label': 'Pass', 'value': 'Pass'},
                ],
                value='Pass')
        ])
        )
    else:
        return html.Div()   
    
@app.callback(Output('checklist-input-3', 'children'),
             [Input('main_dropdown', 'value')])

def update_options_1(main_dropdown):
    if main_dropdown == 'Priority':
        return dbc.Checkbox(label="Priority",
            value=True,
            id="checklist-input-3-2",
        )
    else:
        return html.Div()    

@app.callback(Output('sub_dropdown_3', 'children'),
             [Input('checklist-input-3-2', 'value')])

def update_options_1(main_dropdown):
    if main_dropdown == True:
        return html.Div(
            dbc.Row([
            dcc.Dropdown(
                options=[
                    {'label': 'Low', 'value': 'Low'},
                    {'label': 'Normal', 'value': 'Normal'},
                    {'label': 'High', 'value': 'High'},
                ],
                value='Normal')
            ])
        )
    else:
        return html.Div()     
    
    
if __name__ == " __main__":
    app.run_server(debug=False,port=1115)

```

 ![Screenshot 2022-11-08 162711](https://us1.discourse-cdn.com/flex024/uploads/plot/original/3X/e/2/e215e527354d3e42bc08ee5d4f8da0028616efc0.png)

---

<div class="post-metadata">

**Author:** ![jinnyzor](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/jinnyzor/32/21194_2.png) [@jinnyzor](https://community.plotly.com/u/jinnyzor)\
**Post date:** [November 9, 2022, 4:08am UTC](https://community.plotly.com/t/dynamic-filter-in-dash-python/69679/6 "2022-11-09T04:08:42Z")

</div>

Hello @hoatran / @phuonghao145,

After looking at their layouts, I decided to use what they are using as well, which is a table that is being populated. This is the result:

 ![image](https://us1.discourse-cdn.com/flex024/uploads/plot/original/3X/0/0/004de79cef2b666eced8f7369c8c4b93c3f7ddb7.jpeg)

 ![image](https://us1.discourse-cdn.com/flex024/uploads/plot/original/3X/2/c/2c0dfa9472e385687f9c8f60b1e8a4c34083fd2c.jpeg)

And here is the code:

```auto
import dash
from dash import html, dcc, Input, Output, State, ALL, dash_table, ctx
import dash_bootstrap_components as dbc
import pandas as pd

app = dash.Dash( __name__ )

df = pd.DataFrame({
    'A':[str(i) for i in range(10)],
    'B':[str(i) for i in range(10)],
    'C':[str(i) for i in range(10)],
    'D':[str(i) for i in range(10)],
    'E':[str(i) for i in range(10)],
    'F':[str(i) for i in range(10)],
})

def addFilter(fil = None):
    if not fil:
        fil = 'A'
    newDiv = html.Tr([
        html.Td(dcc.Checklist(id=f'{fil}_check', options=[fil])),
        html.Td(dcc.Dropdown(id=f'{fil}_drop', options=['is', 'is not'])),
        html.Td(dcc.Dropdown(id=f'{fil}_value', options=df[fil].unique())),
    ], style={'width':'75%'})
    return newDiv

app.layout = html.Div([
    html.Fieldset(
        [html.Legend('Filters'),
        html.Table([addFilter()], id='filters', style={'width':'75%'}),
         html.Div(['Add Filter: ',
         dcc.Dropdown(id='addFilter', options=[{'label':i, 'value': i} if i != 'A'
                                               else {'label':i, 'value': i, 'disabled':True} for i in df.columns],
                      style={'width':'150px', 'margin':'5px'}),
        ], style={'width':'250px', 'display': 'flex',
                  'justify-content:':'space-around'}),
         ],
        id='filterField', style={'display':'flex', 'justify-content:':'space-between',
                                 'width':'100%'}),
    html.Fieldset([
        html.Legend('options'),
        html.Button('apply', id='apply'),
        html.Button('clear', id='clear')
    ]),
    html.Fieldset([
        html.Legend('data'),
        dash_table.DataTable(id='myTable', data=df.to_dict('records'),
                             columns=[{'name':i, 'id':i } for i in df.columns])
    ])
])

@app.callback(Output('filters', 'children'),
    Output('addFilter', 'options'),
    Input('addFilter', 'value'),
    Input('clear','n_clicks'),
    State('addFilter', 'options'),
    State('filters', 'children'),
    prevent_initial_call=True
)
def update_options(fil, n1, options, children):
    if ctx.triggered_id == 'clear':
        children = []
        children.append(addFilter())
        for opt in options:
            if fil == opt['value']:
                opt['disabled'] = False
    else:
        if fil:
            for opt in options:
                if fil == opt['value']:
                    opt['disabled'] = True
            children.append(addFilter(fil))
    return children, options

@app.callback(
    Output('myTable', 'data'),
    Input('apply', 'n_clicks'),
    Input('clear', 'n_clicks'),
    State('filters', 'children'),
    prevent_initial_call = True
)
def applyFilters(n1, n2, children):
    new_df = df.copy()
    if ctx.triggered_id == 'clear':
        return new_df.to_dict('records')
    if n1:
        for child in children:
            if 'value' in child['props']['children'][0]['props']['children']['props']:
                if 'value' in child['props']['children'][2]['props']['children']['props']:
                    if 'value' in child['props']['children'][1]['props']['children']['props']:
                        if child['props']['children'][1]['props']['children']['props']['value'] == 'is':
                            new_df = new_df[
                                new_df[child['props']['children'][0]['props']
                                ['children']['props']['id'].split('_')[0]] == child['props']['children']
                                [2]['props']['children']['props']['value']]
                        else:
                            new_df = new_df[
                                new_df[child['props']['children'][0]['props']
                                ['children']['props']['id'].split('_')[0]] != child['props']['children']
                                [2]['props']['children']['props']['value']]
        return new_df.to_dict('records')

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

```

Pattern-matching callbacks would have worked if, we were just testing to see if there was a dropdown value. This is slightly more complicated, because you are testing to see if the value should be used or not. This would have been a lot harder to accomplish via pattern-matching due to the nature of making sure the row stayed together.

---

<div class="post-metadata">

**Author:** ![hoatran](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/hoatran/32/21447_2.png) [@hoatran](https://community.plotly.com/u/hoatran)\
**Post date:** [November 9, 2022, 4:15am UTC](https://community.plotly.com/t/dynamic-filter-in-dash-python/69679/7 "2022-11-09T04:15:20Z")

</div>

Amazing, thank you alot.

---

<div class="post-metadata">

**Author:** ![phuonghao145](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/phuonghao145/32/15805_2.png) [@phuonghao145](https://community.plotly.com/u/phuonghao145)\
**Post date:** [November 10, 2022, 7:48am UTC](https://community.plotly.com/t/dynamic-filter-in-dash-python/69679/8 "2022-11-10T07:48:33Z")

</div>

thank you very much

---

<div class="post-metadata">

**Author:** ![phuonghao145](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/phuonghao145/32/15805_2.png) [@phuonghao145](https://community.plotly.com/u/phuonghao145)\
**Post date:** [November 12, 2022, 4:37am UTC](https://community.plotly.com/t/dynamic-filter-in-dash-python/69679/9 "2022-11-12T04:37:49Z")

</div>

Hi @jinnyzor , I have 2 question :

1. Is there a way to save the filter parameter to the url?
2. I want a new option ‘between’ not only ‘is / is not’.  
when i select ‘is/is not’ it shows a dropdown that allows to select the value of the corresponding column  
and when i choose between it appear 2 dropdown menus that allow to choose the value of the respective column

Please help me . Thanks

---

<div class="post-metadata">

**Author:** ![jinnyzor](https://sea2.discourse-cdn.com/flex024/user_avatar/community.plotly.com/jinnyzor/32/21194_2.png) [@jinnyzor](https://community.plotly.com/u/jinnyzor)\
**Post date:** [November 12, 2022, 5:14am UTC](https://community.plotly.com/t/dynamic-filter-in-dash-python/69679/10 "2022-11-12T05:14:46Z")

</div>

Hello @phuonghao145,

1 - Yes, you can, however, there is a lot going on to try to put the parameters in the url. Filters on and off, is/is not and then the value. Add in the second, and there is quite a bit to parse through.  
2 - It’s better to display this option for only specific columns, so a list of columns that you want to give this ability to will be needed. When between, you mean not including or including the values given?
