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
This commit is contained in:
parent
50de13343b
commit
8d4f79a799
2
AUTHORS
2
AUTHORS
|
@ -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
|
||||||
|
|
|
@ -289,7 +289,7 @@ SESSION_COOKIE_DOMAIN = None # A string like ".lawren
|
||||||
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.
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import os
|
||||||
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
|
||||||
|
|
||||||
|
@ -128,6 +129,62 @@ class SessionBase(object):
|
||||||
|
|
||||||
_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):
|
||||||
|
|
|
@ -4,23 +4,23 @@ from django.core.cache import cache
|
||||||
|
|
||||||
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)
|
|
@ -41,7 +41,7 @@ class SessionStore(SessionBase):
|
||||||
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):
|
||||||
|
|
|
@ -26,14 +26,14 @@ class SessionMiddleware(object):
|
||||||
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,
|
||||||
|
|
|
@ -88,6 +88,100 @@ False
|
||||||
|
|
||||||
>>> 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__':
|
||||||
|
|
|
@ -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()``
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue