| =================== |
| How to use sessions |
| =================== |
| |
| Django provides full support for anonymous sessions. The session framework lets |
| you store and retrieve arbitrary data on a per-site-visitor basis. It stores |
| data on the server side and abstracts the sending and receiving of cookies. |
| Cookies contain a session ID -- not the data itself. |
| |
| Enabling sessions |
| ================= |
| |
| Sessions are implemented via a piece of middleware_ and a Django model. |
| |
| To enable session functionality, do these two things: |
| |
| * Edit the ``MIDDLEWARE_CLASSES`` setting and make sure |
| ``MIDDLEWARE_CLASSES`` contains ``'django.contrib.sessions.middleware.SessionMiddleware'``. |
| The default ``settings.py`` created by ``django-admin.py startproject`` has |
| ``SessionMiddleware`` activated. |
| |
| * Add ``'django.contrib.sessions'`` to your ``INSTALLED_APPS`` setting, and |
| run ``manage.py syncdb`` to install the single database table that stores |
| session data. |
| |
| If you don't want to use sessions, you might as well remove the |
| ``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'`` |
| from your ``INSTALLED_APPS``. It'll save you a small bit of overhead. |
| |
| .. _middleware: ../middleware/ |
| |
| Using sessions in views |
| ======================= |
| |
| When ``SessionMiddleware`` is activated, each ``HttpRequest`` object -- the |
| first argument to any Django view function -- will have a ``session`` |
| attribute, which is a dictionary-like object. You can read it and write to it. |
| |
| It implements the following standard dictionary methods: |
| |
| * ``__getitem__(key)`` |
| Example: ``fav_color = request.session['fav_color']`` |
| |
| * ``__setitem__(key, value)`` |
| Example: ``request.session['fav_color'] = 'blue'`` |
| |
| * ``__delitem__(key)`` |
| Example: ``del request.session['fav_color']``. This raises ``KeyError`` |
| if the given ``key`` isn't already in the session. |
| |
| * ``__contains__(key)`` |
| Example: ``'fav_color' in request.session`` |
| |
| * ``get(key, default=None)`` |
| Example: ``fav_color = request.session.get('fav_color', 'red')`` |
| |
| * ``keys()`` |
| |
| * ``items()`` |
| |
| It also has these three methods: |
| |
| * ``set_test_cookie()`` |
| Sets a test cookie to determine whether the user's browser supports |
| cookies. Due to the way cookies work, you won't be able to test this |
| until the user's next page request. See "Setting test cookies" below for |
| more information. |
| |
| * ``test_cookie_worked()`` |
| Returns either ``True`` or ``False``, depending on whether the user's |
| browser accepted the test cookie. Due to the way cookies work, you'll |
| have to call ``set_test_cookie()`` on a previous, separate page request. |
| See "Setting test cookies" below for more information. |
| |
| * ``delete_test_cookie()`` |
| Deletes the test cookie. Use this to clean up after yourself. |
| |
| You can edit ``request.session`` at any point in your view. You can edit it |
| multiple times. |
| |
| Session object guidelines |
| ------------------------- |
| |
| * Use normal Python strings as dictionary keys on ``request.session``. This |
| is more of a convention than a hard-and-fast rule. |
| |
| * Session dictionary keys that begin with an underscore are reserved for |
| internal use by Django. |
| |
| * Don't override ``request.session`` with a new object, and don't access or |
| set its attributes. Use it like a Python dictionary. |
| |
| Examples |
| -------- |
| |
| This simplistic view sets a ``has_commented`` variable to ``True`` after a user |
| posts a comment. It doesn't let a user post a comment more than once:: |
| |
| def post_comment(request, new_comment): |
| if request.session.get('has_commented', False): |
| return HttpResponse("You've already commented.") |
| c = comments.Comment(comment=new_comment) |
| c.save() |
| request.session['has_commented'] = True |
| return HttpResponse('Thanks for your comment!') |
| |
| This simplistic view logs in a "member" of the site:: |
| |
| def login(request): |
| m = members.get_object(username__exact=request.POST['username']) |
| if m.password == request.POST['password']: |
| request.session['member_id'] = m.id |
| return HttpResponse("You're logged in.") |
| else: |
| return HttpResponse("Your username and password didn't match.") |
| |
| ...And this one logs a member out, according to ``login()`` above:: |
| |
| def logout(request): |
| try: |
| del request.session['member_id'] |
| except KeyError: |
| pass |
| return HttpResponse("You're logged out.") |
| |
| Setting test cookies |
| ==================== |
| |
| As a convenience, Django provides an easy way to test whether the user's |
| browser accepts cookies. Just call ``request.session.set_test_cookie()`` in a |
| view, and call ``request.session.test_cookie_worked()`` in a subsequent view -- |
| not in the same view call. |
| |
| This awkward split between ``set_test_cookie()`` and ``test_cookie_worked()`` |
| is necessary due to the way cookies work. When you set a cookie, you can't |
| actually tell whether a browser accepted it until the browser's next request. |
| |
| It's good practice to use ``delete_test_cookie()`` to clean up after yourself. |
| Do this after you've verified that the test cookie worked. |
| |
| Here's a typical usage example:: |
| |
| def login(request): |
| if request.method == 'POST': |
| if request.session.test_cookie_worked(): |
| request.session.delete_test_cookie() |
| return HttpResponse("You're logged in.") |
| else: |
| return HttpResponse("Please enable cookies and try again.") |
| request.session.set_test_cookie() |
| return render_to_response('foo/login_form.html') |
| |
| Using sessions out of views |
| =========================== |
| |
| Internally, each session is just a normal Django model. The ``Session`` model |
| is defined in ``django/contrib/sessions/models.py``. Because it's a normal |
| model, you can access sessions using the normal Django database API:: |
| |
| >>> from django.contrib.sessions.models import Session |
| >>> s = Session.objects.get_object(pk='2b1189a188b44ad18c35e113ac6ceead') |
| >>> s.expire_date |
| datetime.datetime(2005, 8, 20, 13, 35, 12) |
| |
| Note that you'll need to call ``get_decoded()`` to get the session dictionary. |
| This is necessary because the dictionary is stored in an encoded format:: |
| |
| >>> s.session_data |
| 'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...' |
| >>> s.get_decoded() |
| {'user_id': 42} |
| |
| When sessions are saved |
| ======================= |
| |
| By default, Django only saves to the session database when the session has been |
| modified -- that is if any of its dictionary values have been assigned or |
| deleted:: |
| |
| # Session is modified. |
| request.session['foo'] = 'bar' |
| |
| # Session is modified. |
| del request.session['foo'] |
| |
| # Session is modified. |
| request.session['foo'] = {} |
| |
| # Gotcha: Session is NOT modified, because this alters |
| # request.session['foo'] instead of request.session. |
| request.session['foo']['bar'] = 'baz' |
| |
| To change this default behavior, set the ``SESSION_SAVE_EVERY_REQUEST`` setting |
| to ``True``. If ``SESSION_SAVE_EVERY_REQUEST`` is ``True``, Django will save |
| the session to the database on every single request. |
| |
| Note that the session cookie is only sent when a session has been created or |
| modified. If ``SESSION_SAVE_EVERY_REQUEST`` is ``True``, the session cookie |
| will be sent on every request. |
| |
| Similarly, the ``expires`` part of a session cookie is updated each time the |
| session cookie is sent. |
| |
| Browser-length sessions vs. persistent sessions |
| =============================================== |
| |
| You can control whether the session framework uses browser-length sessions vs. |
| persistent sessions with the ``SESSION_EXPIRE_AT_BROWSER_CLOSE`` setting. |
| |
| By default, ``SESSION_EXPIRE_AT_BROWSER_CLOSE`` is set to ``False``, which |
| means session cookies will be stored in users' browsers for as long as |
| ``SESSION_COOKIE_AGE``. Use this if you don't want people to have to log in |
| every time they open a browser. |
| |
| If ``SESSION_EXPIRE_AT_BROWSER_CLOSE`` is set to ``True``, Django will use |
| browser-length cookies -- cookies that expire as soon as the user closes his or |
| her browser. Use this if you want people to have to log in every time they open |
| a browser. |
| |
| Clearing the session table |
| ========================== |
| |
| Note that session data can accumulate in the ``django_session`` database table |
| and Django does *not* provide automatic purging. Therefore, it's your job to |
| purge expired sessions on a regular basis. |
| |
| To understand this problem, consider what happens when a user uses a session. |
| When a user logs in, Django adds a row to the ``django_session`` database |
| table. Django updates this row each time the session data changes. If the user |
| logs out manually, Django deletes the row. But if the user does *not* log out, |
| the row never gets deleted. |
| |
| Django provides a sample clean-up script in ``django/bin/daily_cleanup.py``. |
| That script deletes any session in the session table whose ``expire_date`` is |
| in the past -- but your application may have different requirements. |
| |
| Settings |
| ======== |
| |
| A few `Django settings`_ give you control over session behavior: |
| |
| SESSION_COOKIE_AGE |
| ------------------ |
| |
| Default: ``1209600`` (2 weeks, in seconds) |
| |
| The age of session cookies, in seconds. |
| |
| SESSION_COOKIE_DOMAIN |
| --------------------- |
| |
| Default: ``None`` |
| |
| The domain to use for session cookies. Set this to a string such as |
| ``".lawrence.com"`` for cross-domain cookies, or use ``None`` for a standard |
| domain cookie. |
| |
| SESSION_COOKIE_NAME |
| ------------------- |
| |
| Default: ``'sessionid'`` |
| |
| The name of the cookie to use for sessions. This can be whatever you want. |
| |
| SESSION_COOKIE_SECURE |
| --------------------- |
| |
| Default: ``False`` |
| |
| Whether to use a secure cookie for the session cookie. If this is set to |
| ``True``, the cookie will be marked as "secure," which means browsers may |
| ensure that the cookie is only sent under an HTTPS connection. |
| |
| SESSION_EXPIRE_AT_BROWSER_CLOSE |
| ------------------------------- |
| |
| Default: ``False`` |
| |
| Whether to expire the session when the user closes his or her browser. See |
| "Browser-length sessions vs. persistent sessions" above. |
| |
| SESSION_SAVE_EVERY_REQUEST |
| -------------------------- |
| |
| Default: ``False`` |
| |
| Whether to save the session data on every request. If this is ``False`` |
| (default), then the session data will only be saved if it has been modified -- |
| that is, if any of its dictionary values have been assigned or deleted. |
| |
| .. _Django settings: ../settings/ |
| |
| Technical details |
| ================= |
| |
| * The session dictionary should accept any pickleable Python object. See |
| `the pickle module`_ for more information. |
| |
| * Session data is stored in a database table named ``django_session`` . |
| |
| * Django only sends a cookie if it needs to. If you don't set any session |
| data, it won't send a session cookie. |
| |
| .. _`the pickle module`: http://www.python.org/doc/current/lib/module-pickle.html |
| |
| Session IDs in URLs |
| =================== |
| |
| The Django sessions framework is entirely, and solely, cookie-based. It does |
| not fall back to putting session IDs in URLs as a last resort, as PHP does. |
| This is an intentional design decision. Not only does that behavior make URLs |
| ugly, it makes your site vulnerable to session-ID theft via the "Referer" |
| header. |