Cookieless Dash app

I wrote an app trying to avoid cookies (and GDPR annoyances), so I did not set programmatically any cookie and I used dcc.Store with storage_type='memory' (hoping this would have helped, correct me if I was wrong, I’m not a cookie expert). Yet, I see that if in the browser (I tried Opera and Firefox) I block cookies altogether, most pages of my app do not work, remaining stuck in “Updating…” state (I mean the title in the browser tab tag). The only pages which load regularly are those which just display some text (like “Under construction”), without callbacks.

In addition to dcc.Store with storage_type='memory' my app is multipage, I use clientside callbacks and @lru_cache decorator.

Do your apps work if you disable completetly cookies in the browser?
If yes, which Dash features do you use?

Thanks in advance.

Hello @nopria,

How did you make sure you are not using any cookies?

As I said I’m not a cookie expert, I hoped to avoid cookies by

  • NOT setting programmatically any cookie, and by
  • using dcc.Store with storage_type='memory'

Both of those happen without cookies and disabling downloading data to the computer.

Can you please post some code where this isn’t happening and causing issues?

I had a small app test that did both of those things without issue. Even used the store in a callback.

I believe if you have things like persistence, you will run into issues unless you set it to be memory as well.

This is the shortest code of one of the pages not working when blocking cookies in browser (I know the code it’s involved, also it was written for Dash < 2.0, I have to update it in order to use some of the new features like duplicate callback outputs):

import config
import dash
import dash_bootstrap_components as dbc

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


dash.register_page(
    __name__,
    path = '/azione-neve',
    title = config.SITE_TITLE+'Azione neve',
    description = 'Calcolo delle azioni della neve su un edificio secondo NTC 2018.',
    order = 1,
)

import random
from fastnumbers import fast_real
import calc.azione_neve_calc as calc
import calc.tools as tls
import calc.common_param as cpar

dropdown_provincia = dcc.Dropdown(
        id="provincia",
        options=list(calc.dict_provincia_zona.keys()),
        clearable=False,
        persistence=True,
        persistence_type = 'memory',
        style={'width':'15em','margin-top':'0.3em'},
    )
input_provincia = html.Div(['Provincia',dropdown_provincia],className='outer-div')
dropdown_provincia.initial = None

radio_coeff_esposizione = dcc.RadioItems(
    id='coeff_esposizione',
    options=tls.dict_to_options({'battuta dai venti':'0.9', 'normalmente esposta ai venti':'1.0', 'riparata dai venti':'1.1'},use_value_as_label=False),
    persistence=True,
    persistence_type = 'memory',
    inputStyle={"margin": "0 0.5em 0 0"},
    labelStyle={'display':'block'},
    style={"margin": "0 1em 0 1.5em"}
    )
input_coeff_esposizione = html.Div([config.linkNTC2018('Esposizione dell\'area ai venti','tab3.4.I'),': ',radio_coeff_esposizione],className='outer-div')
radio_coeff_esposizione.qsv_dict = {'ventosa':'0.9', 'normale':'1.0', 'riparata':'1.1'}
radio_coeff_esposizione.initial = 'normale'

elem_coeff_termico = dcc.Input(
    id='coeff_termico',
    type="number",
    min=0.8, max=1.2, step=0.01,
    persistence=True,
    persistence_type = 'memory',
    style={'width': '5em','text-align':'center','margin-top':'0.3em'}
    )
input_coeff_termico = html.Div([config.linkNTC2018('Coefficiente termico','3.4.5'),' stimato: ',elem_coeff_termico],className='outer-div',style={'width': '12em','text-align':'center'})
elem_coeff_termico.initial = '1.0'


def layout(prov=dropdown_provincia.initial, aslm=cpar.altitudine.initial, ce=radio_coeff_esposizione.initial, ct=elem_coeff_termico.initial, **other_unknown_query_strings):
    cpar.altitudine.value = fast_real(aslm,None)
    dropdown_provincia.value = prov if prov in dropdown_provincia.options else None
    radio_coeff_esposizione.value = radio_coeff_esposizione.qsv_dict.get(ce,None) # if key does not exist return None
    ct = fast_real(ct,1+elem_coeff_termico.max)
    elem_coeff_termico.value = ct if (elem_coeff_termico.min<=ct<=elem_coeff_termico.max) else None
    return dbc.Row(
        [
            html.H5(['Carico neve secondo ',config.linkNTC2018("NTC 2018","3.4"),' su una copertura piana ad una o due falde'], style={'text-align':'center'}),
            html.Div([
                input_provincia,
                cpar.input_altitudine,
                cpar.input_inclinazione_falda,
                input_coeff_esposizione,
                input_coeff_termico,
                html.Div([
                    dbc.Button("Calcola", id="btn_calc_neve", color="primary", className="me-1",disabled=True),
                    dbc.Button("Scarica la relazione tecnica", id="btn_relaz_neve", color="primary", className="me-1",disabled=True)
                    ],style={'margin':'0.5em'}),
                html.Div(dcc.Markdown(id='md_neve',children='Premere il pulsante **\'Calcola\'** dopo aver impostato tutti i parametri di input.', mathjax=True,link_target='_blank'),className='outer-div'),
                dcc.Download(id="download-relaz-neve"),
                cpar.make_modal('modal-neve','Errore inatteso durante la creazione del documento della relazione.',info=True),
                cpar.promemoria_corrispettivo('neve')
                ], style={'text-align':'center'})
            ]
    )


@callback(
    Output('md_neve', 'children'),
    Output("btn_calc_neve", "disabled"),
    Output("btn_relaz_neve", "disabled"),
    Input("btn_calc_neve", "n_clicks"),
    Input('provincia', 'value'),
    Input('altitudine', 'value'),
    Input('inclinazione_falda', 'value'),
    Input('coeff_esposizione', 'value'),
    Input('coeff_termico', 'value'),
    # prevent_initial_call=True,
)
def new_input_and_calc(n_clicks,*args):
    if None in args:
        return 'Parametri di calcolo incompleti o non validi, si prega di verificare.',True,True
    triggered_id = callback_context.triggered[0]['prop_id']
    if triggered_id == 'btn_calc_neve.n_clicks':
        config.log_event(f"pulsante calcolo neve: {args}")
        provincia,altitudine,inclinazione_falda,coeff_esposizione,coeff_termico = args
        return calc.q_neve(provincia,altitudine,inclinazione_falda,float(coeff_esposizione),coeff_termico)[-1],True,False
    else:
        return 'Premere il pulsante **\'Calcola\'** dopo aver impostato tutti i parametri di input.',False,True


@callback(
    Output("download-relaz-neve", "data"),
    Output("modal-neve", "is_open"),
    Output("promemoria_neve", "is_open"),
    Input("btn_relaz_neve", "n_clicks"),
    State('md_neve', 'children'),
    State('provincia', 'value'),
    State('altitudine', 'value'),
    State('inclinazione_falda', 'value'),
    State('coeff_esposizione', 'value'),
    State('coeff_termico', 'value'),
    prevent_initial_call=True,
)
def press_download(n_clicks,txt_md,*filetitleargs):
    filetitle = 'azione-neve_{}_{}_{}_{}_{}'.format(*filetitleargs).replace('.','-').replace(' ','-')
    res = tls.convert_md2odt(txt_md)
    if res is None: # error in converting markdown to odt
        return dash.no_update,True,dash.no_update
    else: # success in converting markdown to odt
        return dcc.send_bytes(res,filetitle+'.odt'),dash.no_update,(True if random.random() < 0.1 else False)

How exactly are you disabling cookies on the browser? Are you also disabling downloading to the computer?

I disable browser cookies from browser settings, in Firefox should be (I don’t have english version);

Preferences / Privacy & Security tab / Enhanced Tracking Protection / Custom option / all cookies

Ok, because I think that also disables downloads at all for any content to go to your computer.

Have you tried running your app in debug mode? Have you tried updating dash to the most recent version? There may be some issues with the older flask servers.

I updated Dash and all Python modules to last version 2 days ago.

I’m not sure about debug mode, my app.py ends with

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

is this enough to run in debug mode? Anyway no error show up when disabling the cookies from browser, just some pages do not load and are stuck in “Updating…”.

A beta of a previous (but very similar) version of my app in online at www.engpad.it

Its really hard to say what is going on without seeing more of your code.

What version of Flask and Werkzeug are you using?

I can really only suggest taking a look at your browser console to see where things are messing up. You can enable cookies and see what is being stored to your local storage and also your cookies on the browser.

Everything that I am trying it continuing to work… Until I have local or session storage.

I found the problem, I did not have the line

persistence_type = 'memory',

in the definition of all elements of input like

input_inclinazione_falda = dcc.Input(
    id='inclinazione_falda',
    type='number',
    min=0, max=75,
    value=0.0,# placeholder="15"
    persistence=True,
    persistence_type = 'memory',
    style={'width': '5em','text-align':'center','margin-top':'0.3em'}
    )

Is there a way to enable persistence_type = 'memory' by default?

By the way, thanks for your time.

2 Likes

I don’t think so, I was thinking that this would really help in your case. Before you mentioned it.

And you are most certainly welcome. :grin: