Code

Ticket #2548: session_middleware.20.diff

File session_middleware.20.diff, 12.6 KB (added by SmileyChris, 6 years ago)

trivial doc change I missed in my recent merge

Line 
1Index: django/contrib/sessions/middleware.py
2===================================================================
3--- django/contrib/sessions/middleware.py       (revision 6980)
4+++ django/contrib/sessions/middleware.py       (working copy)
5@@ -26,12 +26,12 @@
6             if accessed:
7                 patch_vary_headers(response, ('Cookie',))
8             if modified or settings.SESSION_SAVE_EVERY_REQUEST:
9-                if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE:
10+                if request.session.get_expire_at_browser_close():
11                     max_age = None
12                     expires = None
13                 else:
14-                    max_age = settings.SESSION_COOKIE_AGE
15-                    expires_time = time.time() + settings.SESSION_COOKIE_AGE
16+                    max_age = request.session.get_max_age()
17+                    expires_time = time.time() + max_age
18                     expires = cookie_date(expires_time)
19                 # Save the seesion data and refresh the client cookie.
20                 request.session.save()
21Index: django/contrib/sessions/tests.py
22===================================================================
23--- django/contrib/sessions/tests.py    (revision 6980)
24+++ django/contrib/sessions/tests.py    (working copy)
25@@ -88,6 +88,100 @@
26 
27 >>> s.pop('some key', 'does not exist')
28 'does not exist'
29+
30+#########################
31+# Custom session expiry #
32+#########################
33+
34+>>> from django.conf import settings
35+>>> from datetime import datetime, timedelta
36+
37+>>> td10 = timedelta(seconds=10)
38+
39+# A normal session has a max age equal to settings
40+>>> s.get_max_age() == settings.SESSION_COOKIE_AGE
41+True
42+
43+# So does a custom session with an idle expiration time of 0 (but it'll expire
44+# at browser close)
45+>>> s.set_expiry(0)
46+>>> s.get_max_age() == settings.SESSION_COOKIE_AGE
47+True
48+
49+# Custom session idle expiration time
50+>>> s.set_expiry(10)
51+>>> delta = s.get_expiry_date() - datetime.utcnow()
52+>>> delta.seconds in (9, 10)
53+True
54+>>> age = s.get_max_age()
55+>>> age in (9, 10)
56+True
57+
58+# Custom session fixed expiry date (timedelta)
59+>>> s.set_expiry(td10)
60+>>> delta = s.get_expiry_date() - datetime.utcnow()
61+>>> delta.seconds in (9, 10)
62+True
63+>>> age = s.get_max_age()
64+>>> age in (9, 10)
65+True
66+
67+# Custom session fixed expiry date (fixed datetime)
68+>>> s.set_expiry(datetime.utcnow() + td10)
69+>>> delta = s.get_expiry_date() - datetime.utcnow()
70+>>> delta.seconds in (9, 10)
71+True
72+>>> age = s.get_max_age()
73+>>> age in (9, 10)
74+True
75+
76+# Set back to default session age
77+>>> s.set_expiry(None)
78+>>> s.get_max_age() == settings.SESSION_COOKIE_AGE
79+True
80+
81+# Allow to set back to default session age even if no alternate has been set
82+>>> s.set_expiry(None)
83+
84+
85+# We're changing the setting then reverting back to the original setting at the
86+# end of these tests.
87+>>> original_expire_at_browser_close = settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
88+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = False
89+
90+# Custom session age
91+>>> s.set_expiry(10)
92+>>> s.get_expire_at_browser_close()
93+False
94+
95+# Custom expire-at-browser-close
96+>>> s.set_expiry(0)
97+>>> s.get_expire_at_browser_close()
98+True
99+
100+# Default session age
101+>>> s.set_expiry(None)
102+>>> s.get_expire_at_browser_close()
103+False
104+
105+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = True
106+
107+# Custom session age
108+>>> s.set_expiry(10)
109+>>> s.get_expire_at_browser_close()
110+False
111+
112+# Custom expire-at-browser-close
113+>>> s.set_expiry(0)
114+>>> s.get_expire_at_browser_close()
115+True
116+
117+# Default session age
118+>>> s.set_expiry(None)
119+>>> s.get_expire_at_browser_close()
120+True
121+
122+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = original_expire_at_browser_close
123 """
124 
125 if __name__ == '__main__':
126Index: django/contrib/sessions/backends/base.py
127===================================================================
128--- django/contrib/sessions/backends/base.py    (revision 6980)
129+++ django/contrib/sessions/backends/base.py    (working copy)
130@@ -4,6 +4,7 @@
131 import random
132 import sys
133 import time
134+from datetime import datetime, timedelta
135 from django.conf import settings
136 from django.core.exceptions import SuspiciousOperation
137 
138@@ -128,6 +129,54 @@
139 
140     _session = property(_get_session)
141 
142+    def get_max_age(self):
143+        expiry = self.get('_session_expiry')
144+        if not expiry:   # Checks both None and 0 cases
145+            return settings.SESSION_COOKIE_AGE
146+        if not isinstance(expiry, datetime):
147+            return expiry
148+        delta = expiry - datetime.utcnow()
149+        return delta.days * 86400 + delta.seconds
150+
151+    def get_expiry_date(self):
152+        "Returns the expiry date (in UTC)"
153+        expiry = self.get('_session_expiry', settings.SESSION_COOKIE_AGE)
154+        if isinstance(expiry, datetime):
155+            return expiry
156+        return datetime.utcnow() + timedelta(seconds=expiry)
157+
158+    def set_expiry(self, value):
159+        """
160+        Sets a custom expiration for the session. ``value`` can be an integer, a
161+        Python ``datetime`` or ``timedelta`` object or ``None``.
162+
163+        If ``value`` is an integer, the session will expire after that many
164+        seconds of inactivity. If set to ``0`` then the session will expire on
165+        browser close.
166+
167+        If ``value`` is a ``datetime`` or ``timedelta`` object, the session
168+        will expire at that specific future time (``datetime`` objects should be
169+        UTC).
170+
171+        If ``value`` is ``None``, the session uses the global session expiry
172+        policy.
173+        """
174+        if value is None:
175+            # Remove any custom expiration for this session.
176+            try:
177+                del self['_session_expiry']
178+            except KeyError:
179+                pass
180+            return
181+        if isinstance(value, timedelta):
182+            value = datetime.utcnow() + value
183+        self['_session_expiry'] = value
184+
185+    def get_expire_at_browser_close(self):
186+        if self.get('_session_expiry') is None:
187+            return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
188+        return self.get('_session_expiry') == 0
189+
190     # Methods that child classes must implement.
191 
192     def exists(self, session_key):
193Index: django/contrib/sessions/backends/cache.py
194===================================================================
195--- django/contrib/sessions/backends/cache.py   (revision 6980)
196+++ django/contrib/sessions/backends/cache.py   (working copy)
197@@ -4,23 +4,23 @@
198 
199 class SessionStore(SessionBase):
200     """
201-    A cache-based session store.
202+    A cache-based session store.
203     """
204     def __init__(self, session_key=None):
205         self._cache = cache
206         super(SessionStore, self).__init__(session_key)
207-       
208+
209     def load(self):
210         session_data = self._cache.get(self.session_key)
211         return session_data or {}
212 
213     def save(self):
214-        self._cache.set(self.session_key, self._session, settings.SESSION_COOKIE_AGE)
215+        self._cache.set(self.session_key, self._session, self.get_max_age())
216 
217     def exists(self, session_key):
218         if self._cache.get(session_key):
219             return True
220         return False
221-       
222+
223     def delete(self, session_key):
224         self._cache.delete(session_key)
225\ No newline at end of file
226Index: django/contrib/sessions/backends/db.py
227===================================================================
228--- django/contrib/sessions/backends/db.py      (revision 6980)
229+++ django/contrib/sessions/backends/db.py      (working copy)
230@@ -10,16 +10,16 @@
231     """
232     def __init__(self, session_key=None):
233         super(SessionStore, self).__init__(session_key)
234-   
235+
236     def load(self):
237         try:
238             s = Session.objects.get(
239-                session_key = self.session_key,
240-                expire_date__gt=datetime.datetime.now()
241+                session_key = self.session_key,
242+                expire_date__gt=datetime.datetime.utcnow()
243             )
244             return self.decode(s.session_data)
245         except (Session.DoesNotExist, SuspiciousOperation):
246-           
247+
248             # Create a new session_key for extra security.
249             self.session_key = self._get_new_session_key()
250             self._session_cache = {}
251@@ -27,21 +27,21 @@
252             # Save immediately to minimize collision
253             self.save()
254             return {}
255-           
256+
257     def exists(self, session_key):
258         try:
259             Session.objects.get(session_key=session_key)
260         except Session.DoesNotExist:
261             return False
262         return True
263-           
264+
265     def save(self):
266         Session.objects.create(
267             session_key = self.session_key,
268             session_data = self.encode(self._session),
269-            expire_date = datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE)
270+            expire_date = self.get_expiry_date()
271         )
272-   
273+
274     def delete(self, session_key):
275         try:
276             Session.objects.get(session_key=session_key).delete()
277Index: AUTHORS
278===================================================================
279--- AUTHORS     (revision 6980)
280+++ AUTHORS     (working copy)
281@@ -327,7 +327,7 @@
282     tstromberg@google.com
283     Makoto Tsuyuki <mtsuyuki@gmail.com>
284     tt@gurgle.no
285-    Amit Upadhyay
286+    Amit Upadhyay <http://www.amitu.com/blog/>
287     Geert Vanderkelen
288     I.S. van Oostveen <v.oostveen@idca.nl>
289     viestards.lists@gmail.com
290Index: docs/sessions.txt
291===================================================================
292--- docs/sessions.txt   (revision 6980)
293+++ docs/sessions.txt   (working copy)
294@@ -80,19 +80,24 @@
295 It implements the following standard dictionary methods:
296 
297     * ``__getitem__(key)``
298+
299       Example: ``fav_color = request.session['fav_color']``
300 
301     * ``__setitem__(key, value)``
302+
303       Example: ``request.session['fav_color'] = 'blue'``
304 
305     * ``__delitem__(key)``
306+
307       Example: ``del request.session['fav_color']``. This raises ``KeyError``
308       if the given ``key`` isn't already in the session.
309 
310     * ``__contains__(key)``
311+
312       Example: ``'fav_color' in request.session``
313 
314     * ``get(key, default=None)``
315+
316       Example: ``fav_color = request.session.get('fav_color', 'red')``
317 
318     * ``keys()``
319@@ -101,23 +106,65 @@
320 
321     * ``setdefault()`` (**New in Django development version**)
322 
323-It also has these three methods:
324+It also has these methods:
325 
326     * ``set_test_cookie()``
327+
328       Sets a test cookie to determine whether the user's browser supports
329       cookies. Due to the way cookies work, you won't be able to test this
330       until the user's next page request. See "Setting test cookies" below for
331       more information.
332 
333     * ``test_cookie_worked()``
334+
335       Returns either ``True`` or ``False``, depending on whether the user's
336       browser accepted the test cookie. Due to the way cookies work, you'll
337       have to call ``set_test_cookie()`` on a previous, separate page request.
338       See "Setting test cookies" below for more information.
339 
340     * ``delete_test_cookie()``
341+
342       Deletes the test cookie. Use this to clean up after yourself.
343 
344+    * ``set_expiry(value)``
345+
346+      **New in Django development version**
347+
348+      Sets a custom expiration for the session.
349+
350+      If ``value`` is an integer, the session will expire after that many
351+      seconds of inactivity. If set to ``0`` then the session will expire when
352+      the user's browser is closed.
353+
354+      If ``value`` is a ``datetime`` or ``timedelta`` object, the session will
355+      expire at that specific time (``datetime`` objects must be in UTC).
356+
357+      If ``value`` is ``None``, the session reverts to using the global session
358+      expiry policy.
359+
360+    * ``get_max_age()``
361+
362+      **New in Django development version**
363+
364+      Returns the number of seconds until this session expires. For sessions
365+      with no custom expiration (or those set to expire at browser close), this
366+      will equal ``settings.SESSION_COOKIE_AGE``.
367+
368+    * ``get_expiry_date()``
369+
370+      **New in Django development version**
371+
372+      Returns the date this session will expire. For sessions with no custom
373+      expiration (or those set to expire at browser close), this will equal the
374+      UTC date ``settings.SESSION_COOKIE_AGE`` seconds from now.
375+
376+    * ``get_expire_at_browser_close()``
377+
378+      **New in Django development version**
379+
380+      Returns either ``True`` or ``False``, depending on whether this session
381+      will expire when the user's browser is closed.
382+
383 You can edit ``request.session`` at any point in your view. You can edit it
384 multiple times.
385 
386@@ -278,6 +325,12 @@
387 her browser. Use this if you want people to have to log in every time they open
388 a browser.
389 
390+**New in Django development version**
391+
392+This setting is a global default and can be overwritten by explicitly calling
393+``request.session.set_expiry()`` as described above in
394+`using sessions in views`_.
395+
396 Clearing the session table
397 ==========================
398