Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #2548: added get/set_expiry methods to session objects. Thanks,…

… Amit Upadhyay and SmileyChris.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@7586 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 8d4f79a799136edf8190c357e3e0497d7db3ad77 1 parent 50de133
Jacob Kaplan-Moss jacobian authored
2  AUTHORS
View
@@ -360,7 +360,7 @@ answer newbie questions, and generally made Django that much better:
Makoto Tsuyuki <mtsuyuki@gmail.com>
tt@gurgle.no
David Tulig <david.tulig@gmail.com>
- Amit Upadhyay
+ Amit Upadhyay <http://www.amitu.com/blog/>
Geert Vanderkelen
I.S. van Oostveen <v.oostveen@idca.nl>
viestards.lists@gmail.com
2  django/conf/global_settings.py
View
@@ -289,7 +289,7 @@
SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only).
SESSION_COOKIE_PATH = '/' # The path of the session cookie.
SESSION_SAVE_EVERY_REQUEST = False # Whether to save the session data on every request.
-SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether sessions expire when a user closes his browser.
+SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether a user's session cookie expires when they close their browser.
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # The module to store session data
SESSION_FILE_PATH = None # Directory to store session files if using the file session module. If None, the backend will use a sensible default.
57 django/contrib/sessions/backends/base.py
View
@@ -4,6 +4,7 @@
import random
import sys
import time
+from datetime import datetime, timedelta
from django.conf import settings
from django.core.exceptions import SuspiciousOperation
@@ -128,6 +129,62 @@ def _get_session(self):
_session = property(_get_session)
+ def get_expiry_age(self):
+ """Get the number of seconds until the session expires."""
+ expiry = self.get('_session_expiry')
+ if not expiry: # Checks both None and 0 cases
+ return settings.SESSION_COOKIE_AGE
+ if not isinstance(expiry, datetime):
+ return expiry
+ delta = expiry - datetime.now()
+ return delta.days * 86400 + delta.seconds
+
+ def get_expiry_date(self):
+ """Get session the expiry date (as a datetime object)."""
+ expiry = self.get('_session_expiry')
+ if isinstance(expiry, datetime):
+ return expiry
+ if not expiry: # Checks both None and 0 cases
+ expiry = settings.SESSION_COOKIE_AGE
+ return datetime.now() + timedelta(seconds=expiry)
+
+ def set_expiry(self, value):
+ """
+ Sets a custom expiration for the session. ``value`` can be an integer, a
+ Python ``datetime`` or ``timedelta`` object or ``None``.
+
+ If ``value`` is an integer, the session will expire after that many
+ seconds of inactivity. If set to ``0`` then the session will expire on
+ browser close.
+
+ If ``value`` is a ``datetime`` or ``timedelta`` object, the session
+ will expire at that specific future time.
+
+ If ``value`` is ``None``, the session uses the global session expiry
+ policy.
+ """
+ if value is None:
+ # Remove any custom expiration for this session.
+ try:
+ del self['_session_expiry']
+ except KeyError:
+ pass
+ return
+ if isinstance(value, timedelta):
+ value = datetime.now() + value
+ self['_session_expiry'] = value
+
+ def get_expire_at_browser_close(self):
+ """
+ Returns ``True`` if the session is set to expire when the browser
+ closes, and ``False`` if there's an expiry date. Use
+ ``get_expiry_date()`` or ``get_expiry_age()`` to find the actual expiry
+ date/age, if there is one.
+ """
+ if self.get('_session_expiry') is None:
+ return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
+ return self.get('_session_expiry') == 0
+
# Methods that child classes must implement.
def exists(self, session_key):
8 django/contrib/sessions/backends/cache.py
View
@@ -4,23 +4,23 @@
class SessionStore(SessionBase):
"""
- A cache-based session store.
+ A cache-based session store.
"""
def __init__(self, session_key=None):
self._cache = cache
super(SessionStore, self).__init__(session_key)
-
+
def load(self):
session_data = self._cache.get(self.session_key)
return session_data or {}
def save(self):
- self._cache.set(self.session_key, self._session, settings.SESSION_COOKIE_AGE)
+ self._cache.set(self.session_key, self._session, self.get_expiry_age())
def exists(self, session_key):
if self._cache.get(session_key):
return True
return False
-
+
def delete(self, session_key):
self._cache.delete(session_key)
2  django/contrib/sessions/backends/db.py
View
@@ -41,7 +41,7 @@ def save(self):
Session.objects.create(
session_key = self.session_key,
session_data = self.encode(self._session),
- expire_date = datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE)
+ expire_date = self.get_expiry_date()
)
def delete(self, session_key):
8 django/contrib/sessions/middleware.py
View
@@ -26,14 +26,14 @@ def process_response(self, request, response):
if accessed:
patch_vary_headers(response, ('Cookie',))
if modified or settings.SESSION_SAVE_EVERY_REQUEST:
- if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE:
+ if request.session.get_expire_at_browser_close():
max_age = None
expires = None
else:
- max_age = settings.SESSION_COOKIE_AGE
- expires_time = time.time() + settings.SESSION_COOKIE_AGE
+ max_age = request.session.get_expiry_age()
+ expires_time = time.time() + max_age
expires = cookie_date(expires_time)
- # Save the seesion data and refresh the client cookie.
+ # Save the session data and refresh the client cookie.
request.session.save()
response.set_cookie(settings.SESSION_COOKIE_NAME,
request.session.session_key, max_age=max_age,
94 django/contrib/sessions/tests.py
View
@@ -88,6 +88,100 @@
>>> s.pop('some key', 'does not exist')
'does not exist'
+
+#########################
+# Custom session expiry #
+#########################
+
+>>> from django.conf import settings
+>>> from datetime import datetime, timedelta
+
+>>> td10 = timedelta(seconds=10)
+
+# A normal session has a max age equal to settings
+>>> s.get_expiry_age() == settings.SESSION_COOKIE_AGE
+True
+
+# So does a custom session with an idle expiration time of 0 (but it'll expire
+# at browser close)
+>>> s.set_expiry(0)
+>>> s.get_expiry_age() == settings.SESSION_COOKIE_AGE
+True
+
+# Custom session idle expiration time
+>>> s.set_expiry(10)
+>>> delta = s.get_expiry_date() - datetime.now()
+>>> delta.seconds in (9, 10)
+True
+>>> age = s.get_expiry_age()
+>>> age in (9, 10)
+True
+
+# Custom session fixed expiry date (timedelta)
+>>> s.set_expiry(td10)
+>>> delta = s.get_expiry_date() - datetime.now()
+>>> delta.seconds in (9, 10)
+True
+>>> age = s.get_expiry_age()
+>>> age in (9, 10)
+True
+
+# Custom session fixed expiry date (fixed datetime)
+>>> s.set_expiry(datetime.now() + td10)
+>>> delta = s.get_expiry_date() - datetime.now()
+>>> delta.seconds in (9, 10)
+True
+>>> age = s.get_expiry_age()
+>>> age in (9, 10)
+True
+
+# Set back to default session age
+>>> s.set_expiry(None)
+>>> s.get_expiry_age() == settings.SESSION_COOKIE_AGE
+True
+
+# Allow to set back to default session age even if no alternate has been set
+>>> s.set_expiry(None)
+
+
+# We're changing the setting then reverting back to the original setting at the
+# end of these tests.
+>>> original_expire_at_browser_close = settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = False
+
+# Custom session age
+>>> s.set_expiry(10)
+>>> s.get_expire_at_browser_close()
+False
+
+# Custom expire-at-browser-close
+>>> s.set_expiry(0)
+>>> s.get_expire_at_browser_close()
+True
+
+# Default session age
+>>> s.set_expiry(None)
+>>> s.get_expire_at_browser_close()
+False
+
+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = True
+
+# Custom session age
+>>> s.set_expiry(10)
+>>> s.get_expire_at_browser_close()
+False
+
+# Custom expire-at-browser-close
+>>> s.set_expiry(0)
+>>> s.get_expire_at_browser_close()
+True
+
+# Default session age
+>>> s.set_expiry(None)
+>>> s.get_expire_at_browser_close()
+True
+
+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = original_expire_at_browser_close
"""
if __name__ == '__main__':
60 docs/sessions.txt
View
@@ -80,19 +80,24 @@ 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()``
@@ -101,23 +106,70 @@ It implements the following standard dictionary methods:
* ``setdefault()`` (**New in Django development version**)
-It also has these three methods:
+It also has these 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.
+ * ``set_expiry(value)``
+
+ **New in Django development version**
+
+ Sets the expiration time for the session. You can pass a number of
+ different values:
+
+ * If ``value`` is an integer, the session will expire after that
+ many seconds of inactivity. For example, calling
+ ``request.session.set_expiry(300)`` would make the session expire
+ in 5 minutes.
+
+ * If ``value`` is a ``datetime`` or ``timedelta`` object, the
+ session will expire at that specific time.
+
+ * If ``value`` is ``0`` then the user's session cookie will expire
+ when their browser is closed.
+
+ * If ``value`` is ``None``, the session reverts to using the global
+ session expiry policy.
+
+ * ``get_expiry_age()``
+
+ **New in Django development version**
+
+ Returns the number of seconds until this session expires. For sessions
+ with no custom expiration (or those set to expire at browser close), this
+ will equal ``settings.SESSION_COOKIE_AGE``.
+
+ * ``get_expiry_date()``
+
+ **New in Django development version**
+
+ Returns the date this session will expire. For sessions with no custom
+ expiration (or those set to expire at browser close), this will equal the
+ date ``settings.SESSION_COOKIE_AGE`` seconds from now.
+
+ * ``get_expire_at_browser_close()``
+
+ **New in Django development version**
+
+ Returns either ``True`` or ``False``, depending on whether the user's
+ session cookie will expire when their browser is closed.
+
You can edit ``request.session`` at any point in your view. You can edit it
multiple times.
@@ -278,6 +330,12 @@ 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.
+**New in Django development version**
+
+This setting is a global default and can be overwritten at a per-session level
+by explicitly calling ``request.session.set_expiry()`` as described above in
+`using sessions in views`_.
+
Clearing the session table
==========================
Please sign in to comment.
Something went wrong with that request. Please try again.