diff options
author | Alex Gaynor <alex.gaynor@gmail.com> | 2010-08-09 21:22:37 +0000 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2010-08-09 21:22:37 +0000 |
commit | 6001ba016a3db4701d56abc6d30868d4e5d88dbf (patch) | |
tree | 7a42c57d802484300c2384d3cd3a968de1804383 /django/contrib | |
parent | c7bd48cb9f645e5ff07d1e68b86130e3bb2b043f (diff) | |
download | django-soc2010/query-refactor.tar.gz |
[soc2010/query-refactor] Merged up to trunk r13556, resolved merge conflictsarchive/soc2010/query-refactorsoc2010/query-refactor
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/query-refactor@13565 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/contrib')
-rw-r--r-- | django/contrib/admin/options.py | 2 | ||||
-rw-r--r-- | django/contrib/admin/sites.py | 6 | ||||
-rw-r--r-- | django/contrib/admin/templates/admin/auth/user/add_form.html | 5 | ||||
-rw-r--r-- | django/contrib/databrowse/plugins/calendars.py | 2 | ||||
-rw-r--r-- | django/contrib/databrowse/plugins/fieldchoices.py | 2 | ||||
-rw-r--r-- | django/contrib/gis/db/backends/mysql/creation.py | 2 | ||||
-rw-r--r-- | django/contrib/gis/db/models/sql/compiler.py | 4 | ||||
-rw-r--r-- | django/contrib/gis/tests/relatedapp/models.py | 5 | ||||
-rw-r--r-- | django/contrib/gis/tests/relatedapp/tests.py | 10 | ||||
-rw-r--r-- | django/contrib/sessions/backends/file.py | 2 | ||||
-rw-r--r-- | django/contrib/sessions/tests.py | 661 |
11 files changed, 302 insertions, 399 deletions
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 1f8ff6dbd1..d44a5121f8 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -501,7 +501,7 @@ class ModelAdmin(BaseModelAdmin): # Convert the actions into a SortedDict keyed by name # and sorted by description. - actions.sort(lambda a,b: cmp(a[2].lower(), b[2].lower())) + actions.sort(key=lambda k: k[2].lower()) actions = SortedDict([ (name, (func, name, desc)) for func, name, desc in actions diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index 4446490451..d0d6473a34 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -379,11 +379,11 @@ class AdminSite(object): # Sort the apps alphabetically. app_list = app_dict.values() - app_list.sort(lambda x, y: cmp(x['name'], y['name'])) + app_list.sort(key=lambda x: x['name']) # Sort the models alphabetically within each app. for app in app_list: - app['models'].sort(lambda x, y: cmp(x['name'], y['name'])) + app['models'].sort(key=lambda x: x['name']) context = { 'title': _('Site administration'), @@ -443,7 +443,7 @@ class AdminSite(object): if not app_dict: raise http.Http404('The requested admin page does not exist.') # Sort the models alphabetically within each app. - app_dict['models'].sort(lambda x, y: cmp(x['name'], y['name'])) + app_dict['models'].sort(key=lambda x: x['name']) context = { 'title': _('%s administration') % capfirst(app_label), 'app_list': [app_dict], diff --git a/django/contrib/admin/templates/admin/auth/user/add_form.html b/django/contrib/admin/templates/admin/auth/user/add_form.html index b57f59b82e..c8889eb069 100644 --- a/django/contrib/admin/templates/admin/auth/user/add_form.html +++ b/django/contrib/admin/templates/admin/auth/user/add_form.html @@ -2,8 +2,11 @@ {% load i18n %} {% block form_top %} + {% if not is_popup %} <p>{% trans "First, enter a username and password. Then, you'll be able to edit more user options." %}</p> - <input type="hidden" name="_continue" value="1" /> + {% else %} + <p>{% trans "Enter a username and password." %}</p> + {% endif %} {% endblock %} {% block after_field_sets %} diff --git a/django/contrib/databrowse/plugins/calendars.py b/django/contrib/databrowse/plugins/calendars.py index 9bbd02da26..8a08cfd8eb 100644 --- a/django/contrib/databrowse/plugins/calendars.py +++ b/django/contrib/databrowse/plugins/calendars.py @@ -60,7 +60,7 @@ class CalendarPlugin(DatabrowsePlugin): def homepage_view(self, request): easy_model = EasyModel(self.site, self.model) field_list = self.fields.values() - field_list.sort(lambda x, y: cmp(x.verbose_name, y.verbose_name)) + field_list.sort(key=lambda k:k.verbose_name) return render_to_response('databrowse/calendar_homepage.html', {'root_url': self.site.root_url, 'model': easy_model, 'field_list': field_list}) def calendar_view(self, request, field, year=None, month=None, day=None): diff --git a/django/contrib/databrowse/plugins/fieldchoices.py b/django/contrib/databrowse/plugins/fieldchoices.py index 8f77792579..e0c01e95e6 100644 --- a/django/contrib/databrowse/plugins/fieldchoices.py +++ b/django/contrib/databrowse/plugins/fieldchoices.py @@ -61,7 +61,7 @@ class FieldChoicePlugin(DatabrowsePlugin): def homepage_view(self, request): easy_model = EasyModel(self.site, self.model) field_list = self.fields.values() - field_list.sort(lambda x, y: cmp(x.verbose_name, y.verbose_name)) + field_list.sort(key=lambda k: k.verbose_name) return render_to_response('databrowse/fieldchoice_homepage.html', {'root_url': self.site.root_url, 'model': easy_model, 'field_list': field_list}) def field_view(self, request, field, value=None): diff --git a/django/contrib/gis/db/backends/mysql/creation.py b/django/contrib/gis/db/backends/mysql/creation.py index 93fd2e6166..dda77ea6ab 100644 --- a/django/contrib/gis/db/backends/mysql/creation.py +++ b/django/contrib/gis/db/backends/mysql/creation.py @@ -6,7 +6,7 @@ class MySQLCreation(DatabaseCreation): from django.contrib.gis.db.models.fields import GeometryField output = super(MySQLCreation, self).sql_indexes_for_field(model, f, style) - if isinstance(f, GeometryField): + if isinstance(f, GeometryField) and f.spatial_index: qn = self.connection.ops.quote_name db_table = model._meta.db_table idx_name = '%s_%s_id' % (db_table, f.column) diff --git a/django/contrib/gis/db/models/sql/compiler.py b/django/contrib/gis/db/models/sql/compiler.py index 78eeeafe19..55dc4a66ec 100644 --- a/django/contrib/gis/db/models/sql/compiler.py +++ b/django/contrib/gis/db/models/sql/compiler.py @@ -95,7 +95,7 @@ class GeoSQLCompiler(compiler.SQLCompiler): return result def get_default_columns(self, with_aliases=False, col_aliases=None, - start_alias=None, opts=None, as_pairs=False): + start_alias=None, opts=None, as_pairs=False, local_only=False): """ Computes the default columns for selecting every field in the base model. Will sometimes be called to pull in related models (e.g. via @@ -121,6 +121,8 @@ class GeoSQLCompiler(compiler.SQLCompiler): if start_alias: seen = {None: start_alias} for field, model in opts.get_fields_with_model(): + if local_only and model is not None: + continue if start_alias: try: alias = seen[model] diff --git a/django/contrib/gis/tests/relatedapp/models.py b/django/contrib/gis/tests/relatedapp/models.py index 726f9826c0..2e9a62b61f 100644 --- a/django/contrib/gis/tests/relatedapp/models.py +++ b/django/contrib/gis/tests/relatedapp/models.py @@ -38,6 +38,11 @@ class Author(models.Model): name = models.CharField(max_length=100) objects = models.GeoManager() +class Article(models.Model): + title = models.CharField(max_length=100) + author = models.ForeignKey(Author, unique=True) + objects = models.GeoManager() + class Book(models.Model): title = models.CharField(max_length=100) author = models.ForeignKey(Author, related_name='books', null=True) diff --git a/django/contrib/gis/tests/relatedapp/tests.py b/django/contrib/gis/tests/relatedapp/tests.py index 184b65b9c7..5d3d00f08d 100644 --- a/django/contrib/gis/tests/relatedapp/tests.py +++ b/django/contrib/gis/tests/relatedapp/tests.py @@ -4,7 +4,7 @@ from django.contrib.gis.db.models import Collect, Count, Extent, F, Union from django.contrib.gis.geometry.backend import Geometry from django.contrib.gis.tests.utils import mysql, oracle, postgis, spatialite, no_mysql, no_oracle, no_spatialite from django.conf import settings -from models import City, Location, DirectoryEntry, Parcel, Book, Author +from models import City, Location, DirectoryEntry, Parcel, Book, Author, Article cities = (('Aurora', 'TX', -97.516111, 33.058333), ('Roswell', 'NM', -104.528056, 33.387222), @@ -291,6 +291,14 @@ class RelatedGeoModelTest(unittest.TestCase): self.assertEqual(4, len(coll)) self.assertEqual(ref_geom, coll) + def test15_invalid_select_related(self): + "Testing doing select_related on the related name manager of a unique FK. See #13934." + qs = Article.objects.select_related('author__article') + # This triggers TypeError when `get_default_columns` has no `local_only` + # keyword. The TypeError is swallowed if QuerySet is actually + # evaluated as list generation swallows TypeError in CPython. + sql = str(qs.query) + # TODO: Related tests for KML, GML, and distance lookups. def suite(): diff --git a/django/contrib/sessions/backends/file.py b/django/contrib/sessions/backends/file.py index 3f6350345f..843edca9cf 100644 --- a/django/contrib/sessions/backends/file.py +++ b/django/contrib/sessions/backends/file.py @@ -58,7 +58,7 @@ class SessionStore(SessionBase): finally: session_file.close() except IOError: - pass + self.create() return session_data def create(self): diff --git a/django/contrib/sessions/tests.py b/django/contrib/sessions/tests.py index f0a3c4ec8c..d958d4a778 100644 --- a/django/contrib/sessions/tests.py +++ b/django/contrib/sessions/tests.py @@ -1,388 +1,273 @@ -r""" - ->>> from django.conf import settings ->>> 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.cached_db import SessionStore as CacheDBSession ->>> from django.contrib.sessions.backends.file import SessionStore as FileSession ->>> from django.contrib.sessions.backends.base import SessionBase ->>> from django.contrib.sessions.models import Session - ->>> db_session = DatabaseSession() ->>> db_session.modified -False ->>> db_session.get('cat') ->>> 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 - ->>> db_session['foo'] = 'bar' ->>> db_session.save() ->>> db_session.exists(db_session.session_key) -True ->>> prev_key = db_session.session_key ->>> db_session.flush() ->>> db_session.exists(prev_key) -False ->>> db_session.session_key == prev_key -False ->>> db_session.modified, db_session.accessed -(True, True) ->>> db_session['a'], db_session['b'] = 'c', 'd' ->>> db_session.save() ->>> prev_key = db_session.session_key ->>> prev_data = db_session.items() ->>> db_session.cycle_key() ->>> db_session.session_key == prev_key -False ->>> db_session.items() == prev_data -True - -# Submitting an invalid session key (either by guessing, or if the db has -# removed the key) results in a new key being generated. ->>> Session.objects.filter(pk=db_session.session_key).delete() ->>> db_session = DatabaseSession(db_session.session_key) ->>> db_session.save() ->>> DatabaseSession('1').get('cat') - -# -# Cached DB session tests -# - ->>> cdb_session = CacheDBSession() ->>> cdb_session.modified -False ->>> cdb_session['cat'] = "dog" ->>> cdb_session.modified -True ->>> cdb_session.pop('cat') -'dog' ->>> cdb_session.pop('some key', 'does not exist') -'does not exist' ->>> cdb_session.save() ->>> cdb_session.exists(cdb_session.session_key) -True ->>> cdb_session.delete(cdb_session.session_key) ->>> cdb_session.exists(cdb_session.session_key) -False - -# -# File session tests. -# - -# Do file session tests in an isolated directory, and kill it after we're done. ->>> original_session_file_path = settings.SESSION_FILE_PATH ->>> import tempfile ->>> temp_session_store = settings.SESSION_FILE_PATH = tempfile.mkdtemp() - ->>> 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 ->>> FileSession('1').get('cat') - ->>> file_session['foo'] = 'bar' ->>> file_session.save() ->>> file_session.exists(file_session.session_key) -True ->>> prev_key = file_session.session_key ->>> file_session.flush() ->>> file_session.exists(prev_key) -False ->>> file_session.session_key == prev_key -False ->>> file_session.modified, file_session.accessed -(True, True) ->>> file_session['a'], file_session['b'] = 'c', 'd' ->>> file_session.save() ->>> prev_key = file_session.session_key ->>> prev_data = file_session.items() ->>> file_session.cycle_key() ->>> file_session.session_key == prev_key -False ->>> file_session.items() == prev_data -True - ->>> Session.objects.filter(pk=file_session.session_key).delete() ->>> file_session = FileSession(file_session.session_key) ->>> file_session.save() - -# Make sure the file backend checks for a good storage dir ->>> settings.SESSION_FILE_PATH = "/if/this/directory/exists/you/have/a/weird/computer" ->>> FileSession() -Traceback (innermost last): - ... -ImproperlyConfigured: The session storage path '/if/this/directory/exists/you/have/a/weird/computer' doesn't exist. Please set your SESSION_FILE_PATH setting to an existing directory in which Django can store session data. - -# Clean up after the file tests ->>> settings.SESSION_FILE_PATH = original_session_file_path ->>> import shutil ->>> shutil.rmtree(temp_session_store) - -# -# Cache-based tests -# NB: be careful to delete any sessions created; stale sessions fill up the -# /tmp and eventually overwhelm it after lots of runs (think buildbots) -# - ->>> 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 ->>> cache_session['foo'] = 'bar' ->>> cache_session.save() ->>> cache_session.exists(cache_session.session_key) -True ->>> prev_key = cache_session.session_key ->>> cache_session.flush() ->>> cache_session.exists(prev_key) -False ->>> cache_session.session_key == prev_key -False ->>> cache_session.modified, cache_session.accessed -(True, True) ->>> cache_session['a'], cache_session['b'] = 'c', 'd' ->>> cache_session.save() ->>> prev_key = cache_session.session_key ->>> prev_data = cache_session.items() ->>> cache_session.cycle_key() ->>> cache_session.session_key == prev_key -False ->>> cache_session.items() == prev_data -True ->>> cache_session = CacheSession() ->>> cache_session.save() ->>> key = cache_session.session_key ->>> cache_session.exists(key) -True - ->>> Session.objects.filter(pk=cache_session.session_key).delete() ->>> cache_session = CacheSession(cache_session.session_key) ->>> cache_session.save() ->>> cache_session.delete(cache_session.session_key) - ->>> s = SessionBase() ->>> s._session['some key'] = 'exists' # Pre-populate the session with some data ->>> s.accessed = False # Reset to pretend this wasn't accessed previously - ->>> s.accessed, s.modified -(False, False) - ->>> s.pop('non existant key', 'does not exist') -'does not exist' ->>> s.accessed, s.modified -(True, False) - ->>> s.setdefault('foo', 'bar') -'bar' ->>> s.setdefault('foo', 'baz') -'bar' - ->>> s.accessed = False # Reset the accessed flag - ->>> s.pop('some key') -'exists' ->>> s.accessed, s.modified -(True, True) - ->>> s.pop('some key', 'does not exist') -'does not exist' - - ->>> s.get('update key', None) - -# test .update() ->>> s.modified = s.accessed = False # Reset to pretend this wasn't accessed previously ->>> s.update({'update key':1}) ->>> s.accessed, s.modified -(True, True) ->>> s.get('update key', None) -1 - -# test .has_key() ->>> s.modified = s.accessed = False # Reset to pretend this wasn't accessed previously ->>> s.has_key('update key') -True ->>> s.accessed, s.modified -(True, False) - -# test .values() ->>> s = SessionBase() ->>> s.values() -[] ->>> s.accessed -True ->>> s['x'] = 1 ->>> s.values() -[1] - -# test .iterkeys() ->>> s.accessed = False ->>> i = s.iterkeys() ->>> hasattr(i,'__iter__') -True ->>> s.accessed -True ->>> list(i) -['x'] - -# test .itervalues() ->>> s.accessed = False ->>> i = s.itervalues() ->>> hasattr(i,'__iter__') -True ->>> s.accessed -True ->>> list(i) -[1] - -# test .iteritems() ->>> s.accessed = False ->>> i = s.iteritems() ->>> hasattr(i,'__iter__') -True ->>> s.accessed -True ->>> list(i) -[('x', 1)] - -# test .clear() ->>> s.modified = s.accessed = False ->>> s.items() -[('x', 1)] ->>> s.clear() ->>> s.items() -[] ->>> s.accessed, s.modified -(True, True) - -######################### -# 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__': - import doctest - doctest.testmod() +from datetime import datetime, timedelta +from django.conf import settings +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.cached_db import SessionStore as CacheDBSession +from django.contrib.sessions.backends.file import SessionStore as FileSession +from django.contrib.sessions.backends.base import SessionBase +from django.contrib.sessions.models import Session +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase +import shutil +import tempfile +import unittest + + +class SessionTestsMixin(object): + # This does not inherit from TestCase to avoid any tests being run with this + # class, which wouldn't work, and to allow different TestCase subclasses to + # be used. + + backend = None # subclasses must specify + + def setUp(self): + self.session = self.backend() + + def tearDown(self): + # NB: be careful to delete any sessions created; stale sessions fill up + # the /tmp (with some backends) and eventually overwhelm it after lots + # of runs (think buildbots) + self.session.delete() + + def test_new_session(self): + self.assertFalse(self.session.modified) + self.assertFalse(self.session.accessed) + + def test_get_empty(self): + self.assertEqual(self.session.get('cat'), None) + + def test_store(self): + self.session['cat'] = "dog" + self.assertTrue(self.session.modified) + self.assertEqual(self.session.pop('cat'), 'dog') + + def test_pop(self): + self.session['some key'] = 'exists' + # Need to reset these to pretend we haven't accessed it: + self.accessed = False + self.modified = False + + self.assertEqual(self.session.pop('some key'), 'exists') + self.assertTrue(self.session.accessed) + self.assertTrue(self.session.modified) + self.assertEqual(self.session.get('some key'), None) + + def test_pop_default(self): + self.assertEqual(self.session.pop('some key', 'does not exist'), + 'does not exist') + self.assertTrue(self.session.accessed) + self.assertFalse(self.session.modified) + + def test_setdefault(self): + self.assertEqual(self.session.setdefault('foo', 'bar'), 'bar') + self.assertEqual(self.session.setdefault('foo', 'baz'), 'bar') + self.assertTrue(self.session.accessed) + self.assertTrue(self.session.modified) + + def test_update(self): + self.session.update({'update key': 1}) + self.assertTrue(self.session.accessed) + self.assertTrue(self.session.modified) + self.assertEqual(self.session.get('update key', None), 1) + + def test_has_key(self): + self.session['some key'] = 1 + self.session.modified = False + self.session.accessed = False + self.assertTrue(self.session.has_key('some key')) + self.assertTrue(self.session.accessed) + self.assertFalse(self.session.modified) + + def test_values(self): + self.assertEqual(self.session.values(), []) + self.assertTrue(self.session.accessed) + self.session['some key'] = 1 + self.assertEqual(self.session.values(), [1]) + + def test_iterkeys(self): + self.session['x'] = 1 + self.session.modified = False + self.session.accessed = False + i = self.session.iterkeys() + self.assertTrue(hasattr(i, '__iter__')) + self.assertTrue(self.session.accessed) + self.assertFalse(self.session.modified) + self.assertEqual(list(i), ['x']) + + def test_iterkeys(self): + self.session['x'] = 1 + self.session.modified = False + self.session.accessed = False + i = self.session.itervalues() + self.assertTrue(hasattr(i, '__iter__')) + self.assertTrue(self.session.accessed) + self.assertFalse(self.session.modified) + self.assertEqual(list(i), [1]) + + def test_iteritems(self): + self.session['x'] = 1 + self.session.modified = False + self.session.accessed = False + i = self.session.iteritems() + self.assertTrue(hasattr(i, '__iter__')) + self.assertTrue(self.session.accessed) + self.assertFalse(self.session.modified) + self.assertEqual(list(i), [('x',1)]) + + def test_clear(self): + self.session['x'] = 1 + self.session.modified = False + self.session.accessed = False + self.assertEqual(self.session.items(), [('x',1)]) + self.session.clear() + self.assertEqual(self.session.items(), []) + self.assertTrue(self.session.accessed) + self.assertTrue(self.session.modified) + + def test_save(self): + self.session.save() + self.assertTrue(self.session.exists(self.session.session_key)) + + def test_delete(self): + self.session.delete(self.session.session_key) + self.assertFalse(self.session.exists(self.session.session_key)) + + def test_flush(self): + self.session['foo'] = 'bar' + self.session.save() + prev_key = self.session.session_key + self.session.flush() + self.assertFalse(self.session.exists(prev_key)) + self.assertNotEqual(self.session.session_key, prev_key) + self.assertTrue(self.session.modified) + self.assertTrue(self.session.accessed) + + def test_cycle(self): + self.session['a'], self.session['b'] = 'c', 'd' + self.session.save() + prev_key = self.session.session_key + prev_data = self.session.items() + self.session.cycle_key() + self.assertNotEqual(self.session.session_key, prev_key) + self.assertEqual(self.session.items(), prev_data) + + def test_invalid_key(self): + # Submitting an invalid session key (either by guessing, or if the db has + # removed the key) results in a new key being generated. + session = self.backend('1') + session.save() + self.assertNotEqual(session.session_key, '1') + self.assertEqual(session.get('cat'), None) + session.delete() + + # Custom session expiry + def test_default_expiry(self): + # A normal session has a max age equal to settings + self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE) + + # So does a custom session with an idle expiration time of 0 (but it'll + # expire at browser close) + self.session.set_expiry(0) + self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE) + + def test_custom_expiry_seconds(self): + # Using seconds + self.session.set_expiry(10) + delta = self.session.get_expiry_date() - datetime.now() + self.assertTrue(delta.seconds in (9, 10)) + + age = self.session.get_expiry_age() + self.assertTrue(age in (9, 10)) + + def test_custom_expiry_timedelta(self): + # Using timedelta + self.session.set_expiry(timedelta(seconds=10)) + delta = self.session.get_expiry_date() - datetime.now() + self.assertTrue(delta.seconds in (9, 10)) + + age = self.session.get_expiry_age() + self.assertTrue(age in (9, 10)) + + def test_custom_expiry_timedelta(self): + # Using timedelta + self.session.set_expiry(datetime.now() + timedelta(seconds=10)) + delta = self.session.get_expiry_date() - datetime.now() + self.assertTrue(delta.seconds in (9, 10)) + + age = self.session.get_expiry_age() + self.assertTrue(age in (9, 10)) + + def test_custom_expiry_reset(self): + self.session.set_expiry(None) + self.session.set_expiry(10) + self.session.set_expiry(None) + self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE) + + def test_get_expire_at_browser_close(self): + # Tests get_expire_at_browser_close with different settings and different + # set_expiry calls + try: + original_expire_at_browser_close = settings.SESSION_EXPIRE_AT_BROWSER_CLOSE + settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = False + + self.session.set_expiry(10) + self.assertFalse(self.session.get_expire_at_browser_close()) + + self.session.set_expiry(0) + self.assertTrue(self.session.get_expire_at_browser_close()) + + self.session.set_expiry(None) + self.assertFalse(self.session.get_expire_at_browser_close()) + + settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = True + + self.session.set_expiry(10) + self.assertFalse(self.session.get_expire_at_browser_close()) + + self.session.set_expiry(0) + self.assertTrue(self.session.get_expire_at_browser_close()) + + self.session.set_expiry(None) + self.assertTrue(self.session.get_expire_at_browser_close()) + + except: + raise + + finally: + settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = original_expire_at_browser_close + + +class DatabaseSessionTests(SessionTestsMixin, TestCase): + + backend = DatabaseSession + + +class CacheDBSessionTests(SessionTestsMixin, TestCase): + + backend = CacheDBSession + +# Don't need DB flushing for these tests, so can use unittest.TestCase as base class +class FileSessionTests(SessionTestsMixin, unittest.TestCase): + + backend = FileSession + + def setUp(self): + super(FileSessionTests, self).setUp() + # Do file session tests in an isolated directory, and kill it after we're done. + self.original_session_file_path = settings.SESSION_FILE_PATH + self.temp_session_store = settings.SESSION_FILE_PATH = tempfile.mkdtemp() + + def tearDown(self): + settings.SESSION_FILE_PATH = self.original_session_file_path + shutil.rmtree(self.temp_session_store) + super(FileSessionTests, self).tearDown() + + def test_configuration_check(self): + # Make sure the file backend checks for a good storage dir + settings.SESSION_FILE_PATH = "/if/this/directory/exists/you/have/a/weird/computer" + self.assertRaises(ImproperlyConfigured, self.backend) + + +class CacheSessionTests(SessionTestsMixin, unittest.TestCase): + + backend = CacheSession |