From 436862787cbdbd68b0ba20ed8c23b295e3679df3 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Mon, 29 Nov 2021 17:22:23 +0000 Subject: [PATCH] Refs #29708 -- Made SessionBase store expiry as string. --- django/contrib/sessions/backends/base.py | 8 +++++- docs/topics/http/sessions.txt | 6 ++--- tests/sessions_tests/tests.py | 33 ++++++++++-------------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py index 62ee1f0d894..5374965da84 100644 --- a/django/contrib/sessions/backends/base.py +++ b/django/contrib/sessions/backends/base.py @@ -210,8 +210,10 @@ class SessionBase: if not expiry: # Checks both None and 0 cases return self.get_session_cookie_age() - if not isinstance(expiry, datetime): + if not isinstance(expiry, (datetime, str)): return expiry + if isinstance(expiry, str): + expiry = datetime.fromisoformat(expiry) delta = expiry - modification return delta.days * 86400 + delta.seconds @@ -233,6 +235,8 @@ class SessionBase: if isinstance(expiry, datetime): return expiry + elif isinstance(expiry, str): + return datetime.fromisoformat(expiry) expiry = expiry or self.get_session_cookie_age() return modification + timedelta(seconds=expiry) @@ -260,6 +264,8 @@ class SessionBase: return if isinstance(value, timedelta): value = timezone.now() + value + if isinstance(value, datetime): + value = value.isoformat() self['_session_expiry'] = value def get_expire_at_browser_close(self): diff --git a/docs/topics/http/sessions.txt b/docs/topics/http/sessions.txt index 34dabdd5364..ce92af7b711 100644 --- a/docs/topics/http/sessions.txt +++ b/docs/topics/http/sessions.txt @@ -258,10 +258,8 @@ You can edit it multiple times. ``request.session.set_expiry(300)`` would make the session expire in 5 minutes. - * If ``value`` is a ``datetime`` or ``timedelta`` object, the - session will expire at that specific date/time. Note that ``datetime`` - and ``timedelta`` values are only serializable if you are using the - :class:`~django.contrib.sessions.serializers.PickleSerializer`. + * If ``value`` is a ``datetime`` or ``timedelta`` object, the session + will expire at that specific date/time. * If ``value`` is ``0``, the user's session cookie will expire when the user's web browser is closed. diff --git a/tests/sessions_tests/tests.py b/tests/sessions_tests/tests.py index c2743ef48b2..816777bc974 100644 --- a/tests/sessions_tests/tests.py +++ b/tests/sessions_tests/tests.py @@ -333,25 +333,20 @@ class SessionTestsMixin: self.assertEqual(self.session.decode(encoded), {}) def test_actual_expiry(self): - # this doesn't work with JSONSerializer (serializing timedelta) - with override_settings(SESSION_SERIALIZER='django.contrib.sessions.serializers.PickleSerializer'): - self.session = self.backend() # reinitialize after overriding settings - - # Regression test for #19200 - old_session_key = None - new_session_key = None - try: - self.session['foo'] = 'bar' - self.session.set_expiry(-timedelta(seconds=10)) - self.session.save() - old_session_key = self.session.session_key - # With an expiry date in the past, the session expires instantly. - new_session = self.backend(self.session.session_key) - new_session_key = new_session.session_key - self.assertNotIn('foo', new_session) - finally: - self.session.delete(old_session_key) - self.session.delete(new_session_key) + old_session_key = None + new_session_key = None + try: + self.session['foo'] = 'bar' + self.session.set_expiry(-timedelta(seconds=10)) + self.session.save() + old_session_key = self.session.session_key + # With an expiry date in the past, the session expires instantly. + new_session = self.backend(self.session.session_key) + new_session_key = new_session.session_key + self.assertNotIn('foo', new_session) + finally: + self.session.delete(old_session_key) + self.session.delete(new_session_key) def test_session_load_does_not_create_record(self): """