Skip to content

Commit

Permalink
Fixed #2548: added get/set_expiry methods to session objects. Thanks,…
Browse files Browse the repository at this point in the history
… Amit Upadhyay and SmileyChris.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@7586 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
jacobian committed Jun 7, 2008
1 parent 50de133 commit 8d4f79a
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 12 deletions.
2 changes: 1 addition & 1 deletion AUTHORS
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ answer newbie questions, and generally made Django that much better:
Makoto Tsuyuki <mtsuyuki@gmail.com> Makoto Tsuyuki <mtsuyuki@gmail.com>
tt@gurgle.no tt@gurgle.no
David Tulig <david.tulig@gmail.com> David Tulig <david.tulig@gmail.com>
Amit Upadhyay Amit Upadhyay <http://www.amitu.com/blog/>
Geert Vanderkelen Geert Vanderkelen
I.S. van Oostveen <v.oostveen@idca.nl> I.S. van Oostveen <v.oostveen@idca.nl>
viestards.lists@gmail.com viestards.lists@gmail.com
Expand Down
2 changes: 1 addition & 1 deletion django/conf/global_settings.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@
SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only). SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only).
SESSION_COOKIE_PATH = '/' # The path of the session cookie. SESSION_COOKIE_PATH = '/' # The path of the session cookie.
SESSION_SAVE_EVERY_REQUEST = False # Whether to save the session data on every request. 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_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. SESSION_FILE_PATH = None # Directory to store session files if using the file session module. If None, the backend will use a sensible default.


Expand Down
57 changes: 57 additions & 0 deletions django/contrib/sessions/backends/base.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import random import random
import sys import sys
import time import time
from datetime import datetime, timedelta
from django.conf import settings from django.conf import settings
from django.core.exceptions import SuspiciousOperation from django.core.exceptions import SuspiciousOperation


Expand Down Expand Up @@ -128,6 +129,62 @@ def _get_session(self):


_session = property(_get_session) _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. # Methods that child classes must implement.


def exists(self, session_key): def exists(self, session_key):
Expand Down
8 changes: 4 additions & 4 deletions django/contrib/sessions/backends/cache.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@


class SessionStore(SessionBase): class SessionStore(SessionBase):
""" """
A cache-based session store. A cache-based session store.
""" """
def __init__(self, session_key=None): def __init__(self, session_key=None):
self._cache = cache self._cache = cache
super(SessionStore, self).__init__(session_key) super(SessionStore, self).__init__(session_key)

def load(self): def load(self):
session_data = self._cache.get(self.session_key) session_data = self._cache.get(self.session_key)
return session_data or {} return session_data or {}


def save(self): 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): def exists(self, session_key):
if self._cache.get(session_key): if self._cache.get(session_key):
return True return True
return False return False

def delete(self, session_key): def delete(self, session_key):
self._cache.delete(session_key) self._cache.delete(session_key)
2 changes: 1 addition & 1 deletion django/contrib/sessions/backends/db.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def save(self):
Session.objects.create( Session.objects.create(
session_key = self.session_key, session_key = self.session_key,
session_data = self.encode(self._session), 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): def delete(self, session_key):
Expand Down
8 changes: 4 additions & 4 deletions django/contrib/sessions/middleware.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ def process_response(self, request, response):
if accessed: if accessed:
patch_vary_headers(response, ('Cookie',)) patch_vary_headers(response, ('Cookie',))
if modified or settings.SESSION_SAVE_EVERY_REQUEST: 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 max_age = None
expires = None expires = None
else: else:
max_age = settings.SESSION_COOKIE_AGE max_age = request.session.get_expiry_age()
expires_time = time.time() + settings.SESSION_COOKIE_AGE expires_time = time.time() + max_age
expires = cookie_date(expires_time) 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() request.session.save()
response.set_cookie(settings.SESSION_COOKIE_NAME, response.set_cookie(settings.SESSION_COOKIE_NAME,
request.session.session_key, max_age=max_age, request.session.session_key, max_age=max_age,
Expand Down
94 changes: 94 additions & 0 deletions django/contrib/sessions/tests.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -88,6 +88,100 @@
>>> s.pop('some key', 'does not exist') >>> s.pop('some key', 'does not exist')
'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__': if __name__ == '__main__':
Expand Down
60 changes: 59 additions & 1 deletion docs/sessions.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -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: It implements the following standard dictionary methods:


* ``__getitem__(key)`` * ``__getitem__(key)``

Example: ``fav_color = request.session['fav_color']`` Example: ``fav_color = request.session['fav_color']``


* ``__setitem__(key, value)`` * ``__setitem__(key, value)``

Example: ``request.session['fav_color'] = 'blue'`` Example: ``request.session['fav_color'] = 'blue'``


* ``__delitem__(key)`` * ``__delitem__(key)``

Example: ``del request.session['fav_color']``. This raises ``KeyError`` Example: ``del request.session['fav_color']``. This raises ``KeyError``
if the given ``key`` isn't already in the session. if the given ``key`` isn't already in the session.


* ``__contains__(key)`` * ``__contains__(key)``

Example: ``'fav_color' in request.session`` Example: ``'fav_color' in request.session``


* ``get(key, default=None)`` * ``get(key, default=None)``

Example: ``fav_color = request.session.get('fav_color', 'red')`` Example: ``fav_color = request.session.get('fav_color', 'red')``


* ``keys()`` * ``keys()``
Expand All @@ -101,23 +106,70 @@ It implements the following standard dictionary methods:


* ``setdefault()`` (**New in Django development version**) * ``setdefault()`` (**New in Django development version**)


It also has these three methods: It also has these methods:


* ``set_test_cookie()`` * ``set_test_cookie()``

Sets a test cookie to determine whether the user's browser supports 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 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 until the user's next page request. See "Setting test cookies" below for
more information. more information.


* ``test_cookie_worked()`` * ``test_cookie_worked()``

Returns either ``True`` or ``False``, depending on whether the user's Returns either ``True`` or ``False``, depending on whether the user's
browser accepted the test cookie. Due to the way cookies work, you'll 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. have to call ``set_test_cookie()`` on a previous, separate page request.
See "Setting test cookies" below for more information. See "Setting test cookies" below for more information.


* ``delete_test_cookie()`` * ``delete_test_cookie()``

Deletes the test cookie. Use this to clean up after yourself. 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 You can edit ``request.session`` at any point in your view. You can edit it
multiple times. multiple times.


Expand Down Expand Up @@ -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 her browser. Use this if you want people to have to log in every time they open
a browser. 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 Clearing the session table
========================== ==========================


Expand Down

0 comments on commit 8d4f79a

Please sign in to comment.