diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py index 62ee1f0d89..5374965da8 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 34dabdd536..ce92af7b71 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 c2743ef48b..816777bc97 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): """