Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #2066: session data can now be stored in the cache or on the fi…

…lesystem. 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
  • Loading branch information...
commit bcf7e9a9fe037eff4d5dea0cdd8c35104590e1a8 1 parent e6460e4
@jacobian jacobian authored
View
1  AUTHORS
@@ -87,6 +87,7 @@ answer newbie questions, and generally made Django that much better:
Matt Croydon <http://www.postneo.com/>
flavio.curella@gmail.com
Jure Cuhalev <gandalf@owca.info>
+ John D'Agostino <john.dagostino@gmail.com>
dackze+django@gmail.com
David Danier <goliath.mailinglist@gmx.de>
Dirk Datzert <dummy@habmalnefrage.de>
View
14 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 #
View
0  django/contrib/sessions/backends/__init__.py
No changes.
View
143 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
+
View
26 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)
View
49 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
View
67 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
View
89 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
View
3  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')
View
60 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()
View
16 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:
View
93 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
------------------
View
26 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
--------------------------

0 comments on commit bcf7e9a

Please sign in to comment.
Something went wrong with that request. Please try again.