I am storing dictionaries in my session referenced by a string key:
>>> request.session['my_dict'] = {'a': 1, 'b': 2, 'c': 3}
The problem I encountered was that when I modified the dictionary directly, the value would not be changed during the next request:
>>> request.session['my_dict'].pop('c') 3 >>> request.session.has_key('c') False # looks okay... ... # Next request >>> request.session.has_key('c') True # what gives!
Django provides a session framework that lets you store and retrieve data on a per-site-visitor basis. Django abstracts the process of sending and receiving cookies, by placing a session ID cookie on the client side, and storing all the related data on the server side. So the data itself is not stored client side.
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.
As you mentioned in your question, sessions in Django live for as long as SESSION_COOKIE_AGE determines (which defaults to 2 weeks) from the last time it was "accessed". Two exceptions for that: you can set an expiry time to a session yourself, and then it depends on that.
As the documentation states, another option is to use
SESSION_SAVE_EVERY_REQUEST=True
which will make this happen every request anyway. Might be worth it if this happens a lot in your code; I'm guessing the occasional additional overhead wouldn't be much and it is far less than the potential problems from neglecting from including the
request.session.modified = True
line each time.
I apologize for "asking" a question to which I already know the answer, but this was frustrating enough that I thought the answer should be recorded on stackoverflow. If anyone has something to add to my explanation I will award the "answer". I couldn't find the answer by searching based on the problem, but after searching based upon the answer I found that my "problem" is documented behavior. Also turns out another person had this problem.
It turns out that SessionBase is a dictionary-like object that keeps track of when you modify it's keys, and manually sets an attribute modified
(there's also an accessed
). If you mess around with objects within those keys, however, SessionBase has no way to know that the objects are modified, and therefore your changes might not get stored in whatever backend you are using. (I'm using a database backend; I presume this problem applies to all backends, though.) This problem might not apply to models, since the backend is probably storing a reference to the model (and therefore would receive any changes when it loaded the model from the database), but the problem does apply to dictionaries (and perhaps any other base python types that must be stored entirely in the session store.)
The trick is that whenever you modify objects in the session that the session won't notice, you must explicitly tell the session that it is modified:
>>> request.session.modified = True
Hope this helps someone.
The way I got around this was to encapsulate any pop actions on the session into a method that takes care of the details (this method also accepts a view parameter so that session variables can be view-specific):
def session_pop(request, view, key, *args, **kwargs): """ Either returns and removes the value of the key from request.session, or, if request.session[key] is a list, returns the result of a pop on this list. Also, if view is not None, only looks within request.session[view.func_name] so that I can store view-specific session variables. """ # figure out which dictionary we want to operate on. dicto = {} if view is None: dicto = request.session else: if request.session.has_key(view.func_name): dicto = request.session[view.func_name] if dicto.has_key(key): # This is redundant if `dicto == request.session`, but rather than # duplicate the logic to test whether we popped a list underneath # the root level of the session, (which is also determined by `view`) # just explicitly set `modified` # since we certainly modified the session here. request.session.modified = True # Return a non-list if not type(dicto[key]) == type(list()): return dicto.pop(key) # pop a list else: if len(dicto[key]) > 0: return dicto[key].pop() # Parse out a default from the args/kwargs if len(args) > 0: default = args[0] elif kwargs.has_key('default'): default = kwargs['default'] else: # If there wasn't one, complain raise KeyError('Session does not have key "{0}" and no default was provided'.format(key)) return default
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With