Dash-Plotly: keep dropdown selection on page reload - flask

Im quite new to web development in python and am developing a dash-plotly application at the moment. In the application there is a dropdown menu for the user to select a specific time interval for shown data in the graph. When the page is refreshed the selection returns obviously back to default. This simplified code shows the dropdown setup:
import dash_core_components as dcc
app = dash.Dash(__name__)
app.layout = html.Div(
dcc.Store('memory-intervals', storage_type='session')
dcc.Dropdown(
id='time',
options=get_intervals(),
value=Interval.DAY.value,
multi=False,
),
)
What I understood for now is, that I can store data in the browser session through dash's Store component. I managed to store selection like this:
#app.callback(
Output('memory-intervals', 'data'),
Input('time', 'value'),
)
def select_interval(interval):
if interval is None:
raise PreventUpdate
return interval
So I am stuck at this point... how can set the store's data as selection value after page reload?
Thank you in advance!

You could use the Dash persistence feature.
As per documentation:
persistence_type ('local', 'session', or 'memory'; default 'local'):
Where persisted user changes will be stored:
memory: only kept in memory, reset on page refresh. This is useful for
example if you have a tabbed app, that deletes the component when a
different tab is active, and you want changes persisted as you switch
tabs but not after reloading the app.
local: uses window.localStorage. This is the default, and keeps the
data indefinitely within that browser on that computer.
session: uses window.sessionStorage. Like 'local' the data is kept
when you reload the page, but cleared when you close the browser or
open the app in a new browser tab.
In your example, if you need to preserve a dropdown value only after reloading, but not after exiting the browser or closing the tab, you could write:
import dash_core_components as dcc
app = dash.Dash(__name__)
app.layout = html.Div(
dcc.Store('memory-intervals', storage_type='session')
dcc.Dropdown(
id='time',
options=get_intervals(),
value=Interval.DAY.value,
multi=False,
persistence = True,
persistence_type = 'session'
),
)
However, if you need to keep the selection indefinitely within that browser on that computer you could simply use persistence = True because the 'local' type is the default.

I don't know if this is the best solution, but regarding to plotly documentation I managed to do it like this:
#app.callback(
Output('time', 'value'),
Output('memory-intervals', 'data'),
Input('time', 'value'),
Input('memory-intervals', 'data'),
Input('memory-intervals', 'modified_timestamp'),
)
def select_interval(dd_interval, memory_interval, timestamp):
ctx = dash.callback_context
trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]
if trigger_id == 'time':
interval = dd_interval
elif timestamp == -1 or memory_interval is None:
interval = Interval.DAY.value
else:
interval = memory_interval
return interval, interval

Related

How do I check if a user has entered the URL from another website in Django?

I want an effect to be applied when a user is entering my website. So therefore I want to check for when a user is coming from outside my website so the effect isnt getting applied when the user is surfing through different urls inside the website, but only when the user is coming from outside my website
You can't really check for where a user has come from specifically. You can check if the user has just arrived on your site by setting a session variable when they load one of your pages. You can check for it before you set it, and if they don't have it, then they have just arrived and you can apply your effect. There's some good examples of how sessions work here: https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Sessions
There's a couple of ways to handle this. If you are using function based views, you can just create a separate util function and include it at the top of every page, eg,
utils.py
def first_visit(request):
"""returns the answer to the question 'first visit for session?'
make sure SESSION_EXPIRE_AT_BROWSER_CLOSE set to False in settings for persistance"""
if request.session['first_visit']:
#this is not the first session because the session variable is used.
return False
else:
#This is the first visit
...#do something
#set the session variable so you only do the above once
request.session[first_visit'] = True
return True
views.py
from utils.py import first_visit
def show_page(request):
first_visit = first_visit(request)
This approach gives you some control. For example, you may not want to run it on pages that require login, because you will already have run it on the login page.
Otherwise, the best approach depends on what will happen on the first visit. If you want just to update a template (eg, perhaps to show a message or run a script on th epage) you can use a context processor which gives you extra context for your templates. If you want to interrupt the request, perhaps to redirect it to a separate page, you can create a simple piece of middleware.
docs for middleware
docs for context processors
You may also be able to handle this entirely by javascript. This uses localStorage to store whether or not this is the user's first visit to the site and displays the loading area for 5 seconds if there is nothing in localStorage. You can include this in your base template so it runs on every page.
function showMain() {
document.getElementByID("loading").style.display = "none";
document.getElementByID("main").style.display = "block";
}
const secondVisit = localStorage.getItem("secondVisit");
if (!secondVisit) {
//show loading screen
document.getElementByID("loading").style.display = "block";
document.getElementByID("main").style.display = "none";
setTimeout(5000, showMain)
localStorage.setItem("secondVisit", "true" );
} else {
showMain()
}

Flask session cookie reverting

I am sure I am probably being stupid but struggling to wrap my head around this one.
I have a flask website and I am setting up a checkout page for it so users can add their items to the cart etc. Everything was going great, I was able to add items to the cart, get a total etc (using sessions) however when I have tried to implement the ability for users to update the cart on the checkout page, when my form posts, the session data only survives the initial load. The print statement shows the data I am collecting is fine, and the session cookie is set initially, as everything updates, however the moment I then change page, it reverts to whatever it was before I made the update.
#views.route("/shopping-cart", methods=['GET','POST'])
def to_cart():
clear_cart = 'views.to_clear_cart'
if 'shopping' in session:
shopping_list = session['shopping']
sum_list = []
for quantity, price in shopping_list.values():
sum_list.append(quantity * price)
total = sum(sum_list)
if request.method == "POST":
new_quantity = int(request.form.get('quantity'))
product_id = request.form.get('issue')
unit_price = int(request.form.get('price'))
print(new_quantity, product_id, unit_price)
shopping_list[f'{product_id}'] = [new_quantity, unit_price]
return redirect(url_for('views.to_cart'))
return render_template("cart.html",
shopping_list=shopping_list,
total=total,
clear_cart=clear_cart,
)
else:
return render_template("cart.html",
clear_cart=clear_cart
)
I just do not really understand why it is not updating as from what I can tell, the code is running fine, and it does update, but then the session cookie just reverts itself to whatever it was before (using browser side cookies for this for testing).
Any help appreciated!!
After much confusion as everything seemed to be working absolutely fine after I rewrote this in about 5 different ways and printed half the app in the console, I finally found the answer and it is indeed me being an idiot.
It turns out if you modify a value in place rather than creating or deleting it does not automatically save the session state and you just need to state explicitly that it has been modified.
Turns out the answer was as simple as this line of code.
session.modified = True

Storing in Django Sessions

I have a ReactJS component inside a Django template, where a user clicks on a checkout button, posts the item_code and gets redirected to checkout:
onCheckout = () => {
fetch("/onCheckout/", {
method: "POST",
body: JSON.stringify({'item': this.props.item_info.code})
}).then(window.location.replace("/checkout"))
}
A Django view receives the request and stores it in a session.
def onCheckout(request):
if request.method == "POST":
items = request.session.get('items', [])
new_item = json.loads(request.body.decode('utf-8'))['item']
items.append(new_item)
request.session['items'] = items
I am having a issue with storing data in the session. After the first item gets stored correctly in the array, and I then checkout on a second item, the items array starts acting up:
(Pdb) items
['15130BC.ZZ.8042BC.01']
(Pdb) new_item
'5213G-001'
(Pdb) items
['15130BC.ZZ.8042BC.01']
(Pdb) items
['5213G-001']
If I try to access request.session['item'] from any other view function, I get a KeyError.
I am fairly new to Django, any help would be appreciated. Also, I would like to know if there are better alternatives to accomplish the above.
Sessions Config
settings.SESSION_ENGINE = 'django.contrib.sessions.backends.db'
settings.SESSION_CACHE_ALIAS = 'default'
settings.CACHES = {'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}}
Some reading on change detection for Django sessions: https://docs.djangoproject.com/en/2.0/topics/http/sessions/#when-sessions-are-saved
Based on your code, it appears to me that the change detection should happen. However, let's try to brute force this, can you add the following line as the last line of your code: request.session.modified = True - see if this fixes your issue?
Update: some basic checks
Can you verify the following
Check if your db backend is configured priestly
If you want to use a database-backed session, you need to add 'django.contrib.sessions' to your INSTALLED_APPS setting. Once you have configured your installation, run manage.py migrate to install the single database table that stores session data.
Check if your session Middleware is enabled
Sessions are implemented via a piece of middleware. The default settings.py created by django-admin startproject has SessionMiddleware activated. To enable session functionality, edit the MIDDLEWARE_CLASSES setting and make sure it contains 'django.contrib.sessions.middleware.SessionMiddleware'.
Update 2: Test the session
Maybe modify a style existing endpoint as follows and see if you are able to store values and persist them in session :
test_keys = request.session.get('test_keys', [])
test_keys.append(random.randint())
request.session['test_keys'] = test_keys
return Response(request.session.get('test_keys', []))
You should see that each time you hit the api, you get a list with one new integer in it in addition to all past values. Lmk how this goes.

dict in django request.session resets to previous value

According to the task I have to make Django 1.6 chat with Long polling technology (without the use of redis, websokets and so on). For the authorized user I store in the request.session dict with last message ID in each thread (dialogue):
request.session['admin_lastid'] = {'dialogue1': 112, 'dialogue2': 34}
I have two views with long polling. One def get_new(request) scans the ongoing dialogue and updates the dict when a new message:
request.session['admin_lastid']['dialogue1'] = current_thread.lastid
request.session.save()
request.session.modified = True
Another view def scan_threads(request) scans all the dialogue for new messages and this view reads the values from the request.session['admin_lastid'].
But I see the strange behavior of the session. When the view get_new is recorded in the request.session['admin_lastid'] a new value, the next view scan_threads considered the updated value. But when the view get_new start up again, he saw the old value. Why? Can not understand...

Huge remote source to jquery autocomplete without using database

I am working on a web app, in Django, which lets user tell their favourite movies. For the input I want to provide users a textbox with autocomplete enabled.
The list of all movies ever made is very big (18 MB) thus autocomplete has to be enabled using remote source.
To add to this, I have the constraint that I can not use a database. (Because my app is hosted on heroku and to store such data in database would cost a lot)
Right now, I have the list of all movies stored in a .py file, and I import it into my views.py. The view which handles the ajax request from autocomplete iterates over each movie in this list to filter based on the query term and returns the filtered list.
-movies.py
all_movies = [list of all movies' titles] # > 1M string elements
-views.py (handle_autocomplete() gets called whenever user changes the input in the textbox on web app)
import movies
def handle_autocomplete(request):
data = request.GET['term']
my_list = [title for title in movies.all_movies if data in title]
return HttpResponse(simplejson.dumps(my_list))
What are the disadvantages of this approach and how can I improve upon it?
Are there any libraries/ django apps which handle remote-source autocomplete ?