Index: django/test/client.py
===================================================================
--- django/test/client.py	(revision 5456)
+++ django/test/client.py	(working copy)
@@ -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
@@ -129,9 +127,10 @@
     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)
     
@@ -229,24 +228,23 @@
         """
         user = authenticate(**credentials)
         if user 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:
Index: django/conf/global_settings.py
===================================================================
--- django/conf/global_settings.py	(revision 5456)
+++ django/conf/global_settings.py	(working copy)
@@ -270,6 +270,8 @@
 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.engines.db' # The module to store session data
+SESSION_FILE_PATH = '/tmp/'               # Directory to store session files if using the file session module
 
 #########
 # CACHE #
Index: django/contrib/sessions/middleware.py
===================================================================
--- django/contrib/sessions/middleware.py	(revision 5456)
+++ django/contrib/sessions/middleware.py	(working copy)
@@ -1,78 +1,14 @@
 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
 import datetime
 
-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):
-        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):
+    """
+    Django Session Middleware
+    """
     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
@@ -86,21 +22,15 @@
             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
-                    expires = datetime.datetime.strftime(datetime.datetime.utcnow() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE), "%a, %d-%b-%Y %H:%M:%S 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")
+                    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
Index: django/contrib/sessions/engines/base.py
===================================================================
--- django/contrib/sessions/engines/base.py	(revision 0)
+++ django/contrib/sessions/engines/base.py	(revision 0)
@@ -0,0 +1,135 @@
+from django.conf import settings
+from django.utils.cache import patch_vary_headers
+import base64, md5, datetime, random, os, sys
+import cPickle as pickle
+
+TEST_COOKIE_NAME = 'testcookie'
+TEST_COOKIE_VALUE = 'worked'
+
+class SessionBase(object):
+    """
+    MetaClass for all Session classes. 
+    """
+    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[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 encode(self, session_dict):
+        "Returns the given session dictionary pickled and encoded as a string."
+        pickled = pickle.dumps(session_dict)
+        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:
+            from django.core.exceptions import SuspiciousOperation
+            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 person_id and 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)
+    
+    def exists(self, session_key):
+        """
+        returns True if the the session_key already exists in the 
+        session store
+        """
+        raise NotImplementedError
+
+    def save(self):
+        """
+        saves self._session associated self.session_key in the 
+        session store
+        """
+        raise NotImplementedError
+
+    def delete(self, session_key):
+        """
+        deletes the session data associated with session_key
+        """
+        raise NotImplementedError
+
+    def load(self):
+        """
+        returns a dictionary with the session data from the session 
+        store associated with self.session_key
+        """
+        raise NotImplementedError
+        
Index: django/contrib/sessions/engines/__init__.py
===================================================================
Index: django/contrib/sessions/engines/file.py
===================================================================
--- django/contrib/sessions/engines/file.py	(revision 0)
+++ django/contrib/sessions/engines/file.py	(revision 0)
@@ -0,0 +1,63 @@
+import base64, md5, random, sys, datetime, os
+import cPickle as pickle
+from django.conf import settings
+from django.contrib.sessions.models import Session
+from django.contrib.sessions.engines.base import SessionBase
+from django.core.exceptions import SuspiciousOperation
+from django.utils.cache import patch_vary_headers
+import datetime
+
+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):
+        if session_key is None:
+            session_key = self.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 = {}
+                #save to minimize collision
+                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
Index: django/contrib/sessions/engines/cache.py
===================================================================
--- django/contrib/sessions/engines/cache.py	(revision 0)
+++ django/contrib/sessions/engines/cache.py	(revision 0)
@@ -0,0 +1,29 @@
+import base64, md5, random, sys, datetime, os
+from django.conf import settings
+from django.contrib.sessions.engines.base import SessionBase
+from django.core.exceptions import SuspiciousOperation
+from django.utils.cache import patch_vary_headers
+from django.core.cache import cache
+
+class SessionStore(SessionBase):
+    """
+    Implements 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
+
+    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
Index: django/contrib/sessions/engines/db.py
===================================================================
--- django/contrib/sessions/engines/db.py	(revision 0)
+++ django/contrib/sessions/engines/db.py	(revision 0)
@@ -0,0 +1,43 @@
+from django.conf import settings
+from django.contrib.sessions.models import Session
+from django.contrib.sessions.engines.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 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):
+        s = Session(self.session_key, self.encode(self._session), datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE))
+        s.save()
+    
+    def delete(self, session_key):
+        try:
+            s = Session.objects.get(session_key=session_key)
+            s.delete()
+        except Session.DoesNotExist:
+            pass
\ No newline at end of file
Index: django/contrib/sessions/tests.py
===================================================================
--- django/contrib/sessions/tests.py	(revision 5456)
+++ django/contrib/sessions/tests.py	(working copy)
@@ -1,18 +1,60 @@
 r"""
->>> s = SessionWrapper(None)
+>>> db_session = DatabaseSession()
+>>> db_session.modified
+False
+>>> db_session['cat'] = "dog"
+>>> db_session.modified
+True
+>>> db_session.pop('cat')
+'dog'
+>>> db_session.pop('some key', 'does not exist')
+'does not exist'
+>>> db_session.save()
+>>> db_session.exists(db_session.session_key)
+True
+>>> db_session.delete(db_session.session_key)
+>>> db_session.exists(db_session.session_key)
+False
 
-Inject data into the session cache.
->>> s._session_cache = {}
->>> s._session_cache['some key'] = 'exists'
+>>> file_session = FileSession()
+>>> file_session.modified
+False
+>>> file_session['cat'] = "dog"
+>>> file_session.modified
+True
+>>> 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')
-'exists'
-
->>> 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.exists(cache_session.session_key)
+True
+>>> cache_session.delete(cache_session.session_key)
+>>> cache_session.exists(cache_session.session_key)
+False
 """
 
-from django.contrib.sessions.middleware import SessionWrapper
+from django.contrib.sessions.engines.db import SessionStore as DatabaseSession
+from django.contrib.sessions.engines.cache import SessionStore as CacheSession
+from django.contrib.sessions.engines.file import SessionStore as FileSession
+from django.conf import settings
 
 if __name__ == '__main__':
     import doctest
Index: django/contrib/sessions/models.py
===================================================================
--- django/contrib/sessions/models.py	(revision 5456)
+++ django/contrib/sessions/models.py	(working copy)
@@ -1,4 +1,4 @@
-import base64, md5, random, sys, datetime
+import base64, md5, random, sys, datetime, os, time
 import cPickle as pickle
 from django.db import models
 from django.utils.translation import gettext_lazy as _
@@ -14,9 +14,10 @@
     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 person_id and SECRET_KEY as added salt.
+        # Use SECRET_KEY as added salt.
         while 1:
-            session_key = md5.new(str(random.randint(0, sys.maxint - 1)) + str(random.randint(0, sys.maxint - 1)) + settings.SECRET_KEY).hexdigest()
+            session_key = md5.new("%s%s%s%s" % (random.randint(0, sys.maxint - 1), 
+                                    os.getpid(), time.time(), settings.SECRET_KEY)).hexdigest()
             try:
                 self.get(session_key=session_key)
             except self.model.DoesNotExist:
Index: docs/sessions.txt
===================================================================
--- docs/sessions.txt	(revision 5456)
+++ docs/sessions.txt	(working copy)
@@ -10,24 +10,31 @@
 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.
+    * If using the default database session engine add ``'django.contrib.sessions'`` 
+      to your ``INSTALLED_APPS`` setting, and run ``manage.py syncdb`` to install 
+      the single database table that stores session data.
+      
+    * If using the file based session engine set the ``SESSION_FILE_PATH`` setting in
+      ``settings.py`` to the directory that you want sessions stored.
+      
+    * If using the cache based session engine ensure that the Django cache_ framework
+      is configured correctly.
 
 If you don't want to use sessions, you might as well remove the
 ``SessionMiddleware`` line from ``MIDDLEWARE_CLASSES`` and ``'django.contrib.sessions'``
 from your ``INSTALLED_APPS``. It'll save you a small bit of overhead.
 
 .. _middleware: ../middleware/
+.. _cache: ../cache/
 
 Using sessions in views
 =======================
@@ -65,7 +72,7 @@
       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
@@ -153,14 +160,25 @@
 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::
@@ -239,6 +257,37 @@
 
 A few `Django settings`_ give you control over session behavior:
 
+**New in Django development version**
+
+SESSION_ENGINE
+--------------
+
+Default: ``django.contrib.sessions.engines.db``
+
+Django sessions have configurable backends for the method of storage. The 
+setting ``SESSION_ENGINE`` in ``settings.py`` refers to the module which implements 
+the session backend. 
+Django comes with three session engines:
+
+    * ``'django.contrib.sessions.engines.db'`` is the default session storage method.
+      All data is encoded and stored in the database.
+      
+    * ``'django.contrib.sessions.engines.file'`` a simple file based session storage 
+      method. Each session is encoded and stored in individual files on the server.
+      
+    * ``'django.contrib.sessions.engines.cache'`` based on the Django cache_ framework.
+      Sessions are stored in the configured cache backend.
+    
+.. _cache: ../cache/
+
+SESSION_FILE_PATH
+-----------------
+
+Default: ``/tmp/``
+
+If the module associated with ``SESSION_ENGINE`` is ``'django.contrib.sessions.file'`` 
+this is the directory which session files are stored in.
+
 SESSION_COOKIE_AGE
 ------------------
 
