From bcf7e9a9fe037eff4d5dea0cdd8c35104590e1a8 Mon Sep 17 00:00:00 2001 From: Jacob Kaplan-Moss Date: Sat, 15 Sep 2007 21:29:14 +0000 Subject: [PATCH] Fixed #2066: session data can now be stored in the cache or on the filesystem. This should be fully backwards-compatible (the database cache store is still the default). A big thanks to John D'Agostino for the bulk of this code. git-svn-id: http://code.djangoproject.com/svn/django/trunk@6333 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- AUTHORS | 1 + django/conf/global_settings.py | 14 +- django/contrib/sessions/backends/__init__.py | 0 django/contrib/sessions/backends/base.py | 143 +++++++++++++++++++ django/contrib/sessions/backends/cache.py | 26 ++++ django/contrib/sessions/backends/db.py | 49 +++++++ django/contrib/sessions/backends/file.py | 67 +++++++++ django/contrib/sessions/middleware.py | 89 ++---------- django/contrib/sessions/models.py | 3 +- django/contrib/sessions/tests.py | 60 +++++--- django/test/client.py | 16 +-- docs/sessions.txt | 93 ++++++++++-- docs/settings.txt | 26 ++++ 13 files changed, 467 insertions(+), 120 deletions(-) create mode 100644 django/contrib/sessions/backends/__init__.py create mode 100644 django/contrib/sessions/backends/base.py create mode 100644 django/contrib/sessions/backends/cache.py create mode 100644 django/contrib/sessions/backends/db.py create mode 100644 django/contrib/sessions/backends/file.py diff --git a/AUTHORS b/AUTHORS index 1e30399735b27..9d47f2c876440 100644 --- a/AUTHORS +++ b/AUTHORS @@ -87,6 +87,7 @@ answer newbie questions, and generally made Django that much better: Matt Croydon flavio.curella@gmail.com Jure Cuhalev + John D'Agostino dackze+django@gmail.com David Danier Dirk Datzert diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index c8420f3307a6e..b3cbf095c39b7 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -271,12 +271,14 @@ # SESSIONS # ############ -SESSION_COOKIE_NAME = 'sessionid' # Cookie name. This can be whatever you want. -SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2 # Age of cookie, in seconds (default: 2 weeks). -SESSION_COOKIE_DOMAIN = None # A string like ".lawrence.com", or None for standard domain cookie. -SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only). -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_COOKIE_NAME = 'sessionid' # Cookie name. This can be whatever you want. +SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2 # Age of cookie, in seconds (default: 2 weeks). +SESSION_COOKIE_DOMAIN = None # A string like ".lawrence.com", or None for standard domain cookie. +SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only). +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_ENGINE = 'django.contrib.sessions.backends.db' # The module to store session data +SESSION_FILE_PATH = '/tmp/' # Directory to store session files if using the file session module ######### # CACHE # diff --git a/django/contrib/sessions/backends/__init__.py b/django/contrib/sessions/backends/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py new file mode 100644 index 0000000000000..9471706363337 --- /dev/null +++ b/django/contrib/sessions/backends/base.py @@ -0,0 +1,143 @@ +import base64 +import md5 +import os +import random +import sys +import time +from django.conf import settings +from django.core.exceptions import SuspiciousOperation + +try: + import cPickle as pickle +except ImportError: + import pickle + +class SessionBase(object): + """ + Base class for all Session classes. + """ + + TEST_COOKIE_NAME = 'testcookie' + TEST_COOKIE_VALUE = 'worked' + + def __init__(self, session_key=None): + self._session_key = session_key + self.accessed = False + self.modified = False + + def __contains__(self, key): + return key in self._session + + def __getitem__(self, key): + return self._session[key] + + def __setitem__(self, key, value): + self._session[key] = value + self.modified = True + + def __delitem__(self, key): + del self._session[key] + self.modified = True + + def keys(self): + return self._session.keys() + + def items(self): + return self._session.items() + + def get(self, key, default=None): + return self._session.get(key, default) + + def pop(self, key, *args): + return self._session.pop(key, *args) + + def set_test_cookie(self): + self[self.TEST_COOKIE_NAME] = self.TEST_COOKIE_VALUE + + def test_cookie_worked(self): + return self.get(self.TEST_COOKIE_NAME) == self.TEST_COOKIE_VALUE + + def delete_test_cookie(self): + del self[self.TEST_COOKIE_NAME] + + def encode(self, session_dict): + "Returns the given session dictionary pickled and encoded as a string." + pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL) + pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest() + return base64.encodestring(pickled + pickled_md5) + + def decode(self, session_data): + encoded_data = base64.decodestring(session_data) + pickled, tamper_check = encoded_data[:-32], encoded_data[-32:] + if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check: + raise SuspiciousOperation("User tampered with session cookie.") + try: + return pickle.loads(pickled) + # Unpickling can cause a variety of exceptions. If something happens, + # just return an empty dictionary (an empty session). + except: + return {} + + def _get_new_session_key(self): + "Returns session key that isn't being used." + # The random module is seeded when this Apache child is created. + # Use settings.SECRET_KEY as added salt. + while 1: + session_key = md5.new("%s%s%s%s" % (random.randint(0, sys.maxint - 1), + os.getpid(), time.time(), settings.SECRET_KEY)).hexdigest() + if not self.exists(session_key): + break + return session_key + + def _get_session_key(self): + if self._session_key: + return self._session_key + else: + self._session_key = self._get_new_session_key() + return self._session_key + + def _set_session_key(self, session_key): + self._session_key = session_key + + session_key = property(_get_session_key, _set_session_key) + + def _get_session(self): + # Lazily loads session from storage. + self.accessed = True + try: + return self._session_cache + except AttributeError: + if self.session_key is None: + self._session_cache = {} + else: + self._session_cache = self.load() + return self._session_cache + + _session = property(_get_session) + + # Methods that child classes must implement. + + def exists(self, session_key): + """ + Returns True if the given session_key already exists. + """ + raise NotImplementedError + + def save(self): + """ + Saves the session data. + """ + raise NotImplementedError + + def delete(self, session_key): + """ + Clears out the session data under this key. + """ + raise NotImplementedError + + def load(self): + """ + Loads the session data and returns a dictionary. + """ + raise NotImplementedError + diff --git a/django/contrib/sessions/backends/cache.py b/django/contrib/sessions/backends/cache.py new file mode 100644 index 0000000000000..c3e641e69111b --- /dev/null +++ b/django/contrib/sessions/backends/cache.py @@ -0,0 +1,26 @@ +from django.conf import settings +from django.contrib.sessions.backends.base import SessionBase +from django.core.cache import cache + +class SessionStore(SessionBase): + """ + 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) + + 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) \ No newline at end of file diff --git a/django/contrib/sessions/backends/db.py b/django/contrib/sessions/backends/db.py new file mode 100644 index 0000000000000..d1496d63bfb36 --- /dev/null +++ b/django/contrib/sessions/backends/db.py @@ -0,0 +1,49 @@ +from django.conf import settings +from django.contrib.sessions.models import Session +from django.contrib.sessions.backends.base import SessionBase +from django.core.exceptions import SuspiciousOperation +import datetime + +class SessionStore(SessionBase): + """ + Implements database session store + """ + def __init__(self, session_key=None): + super(SessionStore, self).__init__(session_key) + + def load(self): + try: + s = Session.objects.get( + session_key = self.session_key, + expire_date__gt=datetime.datetime.now() + ) + return self.decode(s.session_data) + except (Session.DoesNotExist, SuspiciousOperation): + + # Create a new session_key for extra security. + self.session_key = self._get_new_session_key() + self._session_cache = {} + + # Save immediately to minimize collision + self.save() + return {} + + def exists(self, session_key): + try: + Session.objects.get(session_key=session_key) + except Session.DoesNotExist: + return False + return True + + 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) + ) + + def delete(self, session_key): + try: + Session.objects.get(session_key=session_key).delete() + except Session.DoesNotExist: + pass \ No newline at end of file diff --git a/django/contrib/sessions/backends/file.py b/django/contrib/sessions/backends/file.py new file mode 100644 index 0000000000000..062acca323f01 --- /dev/null +++ b/django/contrib/sessions/backends/file.py @@ -0,0 +1,67 @@ +import os +from django.conf import settings +from django.contrib.sessions.backends.base import SessionBase +from django.core.exceptions import SuspiciousOperation + +class SessionStore(SessionBase): + """ + Implements a file based session store. + """ + def __init__(self, session_key=None): + self.storage_path = settings.SESSION_FILE_PATH + self.file_prefix = settings.SESSION_COOKIE_NAME + super(SessionStore, self).__init__(session_key) + + def _key_to_file(self, session_key=None): + """ + Get the file associated with this session key. + """ + if session_key is None: + session_key = self.session_key + + # Make sure we're not vulnerable to directory traversal. Session keys + # should always be md5s, so they should never contain directory components. + if os.path.sep in session_key: + raise SuspiciousOperation("Invalid characters (directory components) in session key") + + return os.path.join(self.storage_path, self.file_prefix + session_key) + + def load(self): + session_data = {} + try: + session_file = open(self._key_to_file(), "rb") + try: + session_data = self.decode(session_file.read()) + except(EOFError, SuspiciousOperation): + self._session_key = self._get_new_session_key() + self._session_cache = {} + self.save() + finally: + session_file.close() + except(IOError): + pass + return session_data + + def save(self): + try: + f = open(self._key_to_file(self.session_key), "wb") + try: + f.write(self.encode(self._session)) + finally: + f.close() + except(IOError, EOFError): + pass + + def exists(self, session_key): + if os.path.exists(self._key_to_file(session_key)): + return True + return False + + def delete(self, session_key): + try: + os.unlink(self._key_to_file(session_key)) + except OSError: + pass + + def clean(self): + pass \ No newline at end of file diff --git a/django/contrib/sessions/middleware.py b/django/contrib/sessions/middleware.py index 2531c8e244bc0..4c3c5acc43abd 100644 --- a/django/contrib/sessions/middleware.py +++ b/django/contrib/sessions/middleware.py @@ -1,6 +1,4 @@ from django.conf import settings -from django.contrib.sessions.models import Session -from django.core.exceptions import SuspiciousOperation from django.utils.cache import patch_vary_headers from email.Utils import formatdate import datetime @@ -9,73 +7,11 @@ TEST_COOKIE_NAME = 'testcookie' TEST_COOKIE_VALUE = 'worked' -class SessionWrapper(object): - def __init__(self, session_key): - self.session_key = session_key - self.accessed = False - self.modified = False - - def __contains__(self, key): - return key in self._session - - def __getitem__(self, key): - return self._session[key] - - def __setitem__(self, key, value): - self._session[key] = value - self.modified = True - - def __delitem__(self, key): - del self._session[key] - self.modified = True - - def keys(self): - return self._session.keys() - - def items(self): - return self._session.items() - - def get(self, key, default=None): - return self._session.get(key, default) - - def pop(self, key, *args): - self.modified = self.modified or key in self._session - return self._session.pop(key, *args) - - def set_test_cookie(self): - self[TEST_COOKIE_NAME] = TEST_COOKIE_VALUE - - def test_cookie_worked(self): - return self.get(TEST_COOKIE_NAME) == TEST_COOKIE_VALUE - - def delete_test_cookie(self): - del self[TEST_COOKIE_NAME] - - def _get_session(self): - # Lazily loads session from storage. - self.accessed = True - try: - return self._session_cache - except AttributeError: - if self.session_key is None: - self._session_cache = {} - else: - try: - s = Session.objects.get(session_key=self.session_key, - expire_date__gt=datetime.datetime.now()) - self._session_cache = s.get_decoded() - except (Session.DoesNotExist, SuspiciousOperation): - self._session_cache = {} - # Set the session_key to None to force creation of a new - # key, for extra security. - self.session_key = None - return self._session_cache - - _session = property(_get_session) - class SessionMiddleware(object): + def process_request(self, request): - request.session = SessionWrapper(request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)) + engine = __import__(settings.SESSION_ENGINE, {}, {}, ['']) + request.session = engine.SessionStore(request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)) def process_response(self, request, response): # If request.session was modified, or if response.session was set, save @@ -89,25 +25,22 @@ def process_response(self, request, response): if accessed: patch_vary_headers(response, ('Cookie',)) if modified or settings.SESSION_SAVE_EVERY_REQUEST: - if request.session.session_key: - session_key = request.session.session_key - else: - obj = Session.objects.get_new_session_object() - session_key = obj.session_key - if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE: max_age = None expires = None else: max_age = settings.SESSION_COOKIE_AGE rfcdate = formatdate(time.time() + settings.SESSION_COOKIE_AGE) + # Fixed length date must have '-' separation in the format # DD-MMM-YYYY for compliance with Netscape cookie standard - expires = (rfcdate[:7] + "-" + rfcdate[8:11] - + "-" + rfcdate[12:26] + "GMT") - new_session = Session.objects.save(session_key, request.session._session, - datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE)) - response.set_cookie(settings.SESSION_COOKIE_NAME, session_key, + expires = datetime.datetime.strftime(datetime.datetime.utcnow() + \ + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE), "%a, %d-%b-%Y %H:%M:%S GMT") + + # Save the seesion data and refresh the client cookie. + request.session.save() + response.set_cookie(settings.SESSION_COOKIE_NAME, request.session.session_key, max_age=max_age, expires=expires, domain=settings.SESSION_COOKIE_DOMAIN, secure=settings.SESSION_COOKIE_SECURE or None) + return response diff --git a/django/contrib/sessions/models.py b/django/contrib/sessions/models.py index fda10c9743d73..c0863969478cc 100644 --- a/django/contrib/sessions/models.py +++ b/django/contrib/sessions/models.py @@ -1,4 +1,4 @@ -import base64, md5, random, sys, datetime, os, time +import base64, md5, random, sys, datetime import cPickle as pickle from django.db import models from django.utils.translation import ugettext_lazy as _ @@ -74,6 +74,7 @@ class Session(models.Model): session_data = models.TextField(_('session data')) expire_date = models.DateTimeField(_('expire date')) objects = SessionManager() + class Meta: db_table = 'django_session' verbose_name = _('session') diff --git a/django/contrib/sessions/tests.py b/django/contrib/sessions/tests.py index e83442123ea23..cfb475fc498e9 100644 --- a/django/contrib/sessions/tests.py +++ b/django/contrib/sessions/tests.py @@ -1,35 +1,59 @@ r""" ->>> s = SessionWrapper(None) -Inject data into the session cache. ->>> s._session_cache = {} ->>> s._session_cache['some key'] = 'exists' +>>> from django.contrib.sessions.backends.db import SessionStore as DatabaseSession +>>> from django.contrib.sessions.backends.cache import SessionStore as CacheSession +>>> from django.contrib.sessions.backends.file import SessionStore as FileSession ->>> s.accessed +>>> db_session = DatabaseSession() +>>> db_session.modified False ->>> s.modified -False - ->>> s.pop('non existant key', 'does not exist') +>>> db_session['cat'] = "dog" +>>> db_session.modified +True +>>> db_session.pop('cat') +'dog' +>>> db_session.pop('some key', 'does not exist') 'does not exist' ->>> s.accessed +>>> db_session.save() +>>> db_session.exists(db_session.session_key) True ->>> s.modified +>>> db_session.delete(db_session.session_key) +>>> db_session.exists(db_session.session_key) False ->>> s.pop('some key') -'exists' ->>> s.accessed +>>> file_session = FileSession() +>>> file_session.modified +False +>>> file_session['cat'] = "dog" +>>> file_session.modified True ->>> s.modified +>>> file_session.pop('cat') +'dog' +>>> file_session.pop('some key', 'does not exist') +'does not exist' +>>> file_session.save() +>>> file_session.exists(file_session.session_key) True +>>> file_session.delete(file_session.session_key) +>>> file_session.exists(file_session.session_key) +False ->>> s.pop('some key', 'does not exist') +>>> cache_session = CacheSession() +>>> cache_session.modified +False +>>> cache_session['cat'] = "dog" +>>> cache_session.modified +True +>>> cache_session.pop('cat') +'dog' +>>> cache_session.pop('some key', 'does not exist') 'does not exist' +>>> cache_session.save() +>>> cache_session.delete(cache_session.session_key) +>>> cache_session.exists(cache_session.session_key) +False """ -from django.contrib.sessions.middleware import SessionWrapper - if __name__ == '__main__': import doctest doctest.testmod() diff --git a/django/test/client.py b/django/test/client.py index faacc5bf9ecc8..8f50f5aef76f7 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -4,8 +4,6 @@ from urlparse import urlparse from django.conf import settings from django.contrib.auth import authenticate, login -from django.contrib.sessions.models import Session -from django.contrib.sessions.middleware import SessionWrapper from django.core.handlers.base import BaseHandler from django.core.handlers.wsgi import WSGIRequest from django.core.signals import got_request_exception @@ -132,9 +130,10 @@ def store_exc_info(self, *args, **kwargs): def _session(self): "Obtain the current session variables" if 'django.contrib.sessions' in settings.INSTALLED_APPS: + engine = __import__(settings.SESSION_ENGINE, {}, {}, ['']) cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None) if cookie: - return SessionWrapper(cookie.value) + return engine.SessionClass(cookie.value) return {} session = property(_session) @@ -247,24 +246,23 @@ def login(self, **credentials): """ user = authenticate(**credentials) if user and user.is_active and 'django.contrib.sessions' in settings.INSTALLED_APPS: - obj = Session.objects.get_new_session_object() + engine = __import__(settings.SESSION_ENGINE, {}, {}, ['']) # Create a fake request to store login details request = HttpRequest() - request.session = SessionWrapper(obj.session_key) + request.session = engine.SessionClass() login(request, user) # Set the cookie to represent the session - self.cookies[settings.SESSION_COOKIE_NAME] = obj.session_key + self.cookies[settings.SESSION_COOKIE_NAME] = request.session.session_key self.cookies[settings.SESSION_COOKIE_NAME]['max-age'] = None self.cookies[settings.SESSION_COOKIE_NAME]['path'] = '/' self.cookies[settings.SESSION_COOKIE_NAME]['domain'] = settings.SESSION_COOKIE_DOMAIN self.cookies[settings.SESSION_COOKIE_NAME]['secure'] = settings.SESSION_COOKIE_SECURE or None self.cookies[settings.SESSION_COOKIE_NAME]['expires'] = None - # Set the session values - Session.objects.save(obj.session_key, request.session._session, - datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE)) + # Save the session values + request.session.save() return True else: diff --git a/docs/sessions.txt b/docs/sessions.txt index 96a88c617af24..023285f704d85 100644 --- a/docs/sessions.txt +++ b/docs/sessions.txt @@ -10,18 +10,21 @@ Cookies contain a session ID -- not the data itself. Enabling sessions ================= -Sessions are implemented via a piece of middleware_ and a Django model. +Sessions are implemented via a piece of middleware_. -To enable session functionality, do these two things: +To enable session functionality, do the following: * 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. + + * Add ``'django.contrib.sessions'`` to your ``INSTALLED_APPS`` setting, + and run ``manage.py syncdb`` to install the single database table + that stores session data. + + **New in development version**: this step is optional if you're not using + the database session backend; see `configuring the session engine`_. If you don't want to use sessions, you might as well remove the ``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'`` @@ -29,6 +32,44 @@ from your ``INSTALLED_APPS``. It'll save you a small bit of overhead. .. _middleware: ../middleware/ +Configuring the session engine +============================== + +**New in development version**. + +By default, Django stores sessions in your database (using the model +``django.contrib.sessions.models.Session``). Though this is convenient, in +some setups it's faster to store session data elsewhere, so Django can be +configured to store session data on your filesystem or in your cache. + +Using file-based sessions +------------------------- + +To use file-based sessions, set the ``SESSION_ENGINE`` setting to +``"django.contrib.sessions.backends.file"``. + +You might also want to set the ``SESSION_FILE_PATH`` setting (which +defaults to ``/tmp``) to control where Django stores session files. Be +sure to check that your web server has permissions to read and write to +this location. + +Using cache-based sessions +-------------------------- + +To store session data using Django's cache system, set ``SESSION_ENGINE`` +to ``"django.contrib.sessions.backends.cache"``. You'll want to make sure +you've configured your cache; see the `cache documentation`_ for details. + +.. _cache documentation: ../cache/ + +.. note:: + + You probably don't want to use cache-based sessions if you're not using + the memcached cache backend. The local memory and simple cache backends + don't retain data long enough to be good choices, and it'll be faster + to use file or database sessions directly instead of sending everything + through the file or database cache backends. + Using sessions in views ======================= @@ -153,14 +194,25 @@ Here's a typical usage example:: Using sessions out of views =========================== -Internally, each session is just a normal Django model. The ``Session`` model +The ``SessionStore`` which implements the session storage method can be imported +and a API is available to manipulate the session data outside of a view:: + + >>> from django.contrib.sessions.engines.db import SessionStore + >>> s = SessionStore(session_key='2b1189a188b44ad18c35e113ac6ceead') + >>> s['last_login'] = datetime.datetime(2005, 8, 20, 13, 35, 10) + >>> s['last_login'] + datetime.datetime(2005, 8, 20, 13, 35, 0) + >>> s.save() + +Or if you are using the ``django.contrib.sessions.engine.db`` 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(pk='2b1189a188b44ad18c35e113ac6ceead') >>> s.expire_date - datetime.datetime(2005, 8, 20, 13, 35, 12) + 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:: @@ -245,6 +297,31 @@ Settings A few `Django settings`_ give you control over session behavior: +SESSION_ENGINE +-------------- + +**New in Django development version** + +Default: ``django.contrib.sessions.backends.db`` + +Controls where Django stores session data. Valid values are: + + * ``'django.contrib.sessions.backends.db'`` + * ``'django.contrib.sessions.backends.file'`` + * ``'django.contrib.sessions.backends.cache'`` + +See `configuring the session engine`_ for more details. + +SESSION_FILE_PATH +----------------- + +**New in Django development version** + +Default: ``/tmp/`` + +If you're using file-based session storage, this sets the directory in +which Django will store session data. + SESSION_COOKIE_AGE ------------------ diff --git a/docs/settings.txt b/docs/settings.txt index 2e6185f4447d4..46fdd70258065 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -733,6 +733,21 @@ Default: ``'root@localhost'`` The e-mail address that error messages come from, such as those sent to ``ADMINS`` and ``MANAGERS``. +SESSION_ENGINE +-------------- + +**New in Django development version** + +Default: ``django.contrib.sessions.backends.db`` + +Controls where Django stores session data. Valid values are: + + * ``'django.contrib.sessions.backends.db'`` + * ``'django.contrib.sessions.backends.file'`` + * ``'django.contrib.sessions.backends.cache'`` + +See the `session docs`_ for more details. + SESSION_COOKIE_AGE ------------------ @@ -775,6 +790,17 @@ Default: ``False`` Whether to expire the session when the user closes his or her browser. See the `session docs`_. +SESSION_FILE_PATH +----------------- + +**New in Django development version** + +Default: ``/tmp/`` + +If you're using file-based session storage, this sets the directory in +which Django will store session data. See the `session docs`_ for +more details. + SESSION_SAVE_EVERY_REQUEST --------------------------