Code

Ticket #4604: messages.diff

File messages.diff, 11.9 KB (added by SmileyChris, 6 years ago)

Rewritten version, new core context processor which grabs messages from both user and session

Line 
1Index: django/conf/global_settings.py
2===================================================================
3--- django/conf/global_settings.py      (revision 6894)
4+++ django/conf/global_settings.py      (working copy)
5@@ -154,6 +154,7 @@
6     'django.core.context_processors.debug',
7     'django.core.context_processors.i18n',
8     'django.core.context_processors.media',
9+    'django.core.context_processors.messages',
10 #    'django.core.context_processors.request',
11 )
12 
13Index: django/core/context_processors.py
14===================================================================
15--- django/core/context_processors.py   (revision 6894)
16+++ django/core/context_processors.py   (working copy)
17@@ -8,6 +8,7 @@
18 """
19 
20 from django.conf import settings
21+from django.utils.functional import memoize, lazy
22 
23 def auth(request):
24     """
25@@ -24,10 +25,45 @@
26         user = AnonymousUser()
27     return {
28         'user': user,
29-        'messages': user.get_and_delete_messages(),
30         'perms': PermWrapper(user),
31     }
32 
33+class LazyMessages(object):
34+    def __init__(self, request):
35+        self.request = request
36+    def __iter__(self):
37+        return self.messages.__iter__()
38+    def __len__(self):
39+        return len(self.messages)
40+    def __nonzero__(self):
41+        return bool(self.messages)
42+    def __unicode__(self):
43+        return unicode(self.messages)
44+    def _get_messages(self):
45+        if hasattr(self, '_messages'):
46+            return self._messages
47+        # First, retreive any messages for the user.
48+        if hasattr(self.request, 'user') and \
49+           hasattr(self.request.user, 'get_and_delete_messages'):
50+            self._messages = self.request.user.get_and_delete_messages()
51+        else:
52+            self._messages = []
53+        # Next, retrieve any messages for the session.
54+        self._messages += self.request.session.get_and_delete_messages()
55+        return self._messages
56+    messages = property(_get_messages)
57+
58+def messages(request):
59+    """
60+    Return messages for the session and the current user.
61+
62+    The messages are lazy loaded, so no messages are retreived and deleted
63+    unless requested from the template.
64+    """
65+    return {
66+        'messages': LazyMessages(request)
67+    }
68+
69 def debug(request):
70     "Returns context variables helpful for debugging."
71     context_extras = {}
72Index: django/contrib/sessions/tests.py
73===================================================================
74--- django/contrib/sessions/tests.py    (revision 6894)
75+++ django/contrib/sessions/tests.py    (working copy)
76@@ -16,6 +16,19 @@
77 'dog'
78 >>> db_session.pop('some key', 'does not exist')
79 'does not exist'
80+>>> db_session.get_messages()
81+[]
82+>>> db_session.create_message('first post')
83+>>> db_session.get_messages()
84+['first post']
85+>>> db_session.get_and_delete_messages()
86+['first post']
87+>>> db_session.get_and_delete_messages()
88+[]
89+>>> db_session.create_message('hello')
90+>>> db_session.create_message('world')
91+>>> db_session.get_and_delete_messages()
92+['hello', 'world']
93 >>> db_session.save()
94 >>> db_session.exists(db_session.session_key)
95 True
96@@ -33,6 +46,19 @@
97 'dog'
98 >>> file_session.pop('some key', 'does not exist')
99 'does not exist'
100+>>> file_session.get_messages()
101+[]
102+>>> file_session.create_message('first post')
103+>>> file_session.get_messages()
104+['first post']
105+>>> file_session.get_and_delete_messages()
106+['first post']
107+>>> file_session.get_and_delete_messages()
108+[]
109+>>> file_session.create_message('hello')
110+>>> file_session.create_message('world')
111+>>> file_session.get_and_delete_messages()
112+['hello', 'world']
113 >>> file_session.save()
114 >>> file_session.exists(file_session.session_key)
115 True
116@@ -57,6 +83,19 @@
117 'dog'
118 >>> cache_session.pop('some key', 'does not exist')
119 'does not exist'
120+>>> cache_session.get_messages()
121+[]
122+>>> cache_session.create_message('first post')
123+>>> cache_session.get_messages()
124+['first post']
125+>>> cache_session.get_and_delete_messages()
126+['first post']
127+>>> cache_session.get_and_delete_messages()
128+[]
129+>>> cache_session.create_message('hello')
130+>>> cache_session.create_message('world')
131+>>> cache_session.get_and_delete_messages()
132+['hello', 'world']
133 >>> cache_session.save()
134 >>> cache_session.delete(cache_session.session_key)
135 >>> cache_session.exists(cache_session.session_key)
136Index: django/contrib/sessions/backends/base.py
137===================================================================
138--- django/contrib/sessions/backends/base.py    (revision 6894)
139+++ django/contrib/sessions/backends/base.py    (working copy)
140@@ -18,6 +18,7 @@
141     """
142     TEST_COOKIE_NAME = 'testcookie'
143     TEST_COOKIE_VALUE = 'worked'
144+    MESSAGES_NAME = '_messages'
145 
146     def __init__(self, session_key=None):
147         self._session_key = session_key
148@@ -68,6 +69,20 @@
149     def delete_test_cookie(self):
150         del self[self.TEST_COOKIE_NAME]
151 
152+    def get_messages(self):
153+            return self.get(self.MESSAGES_NAME, [])
154+
155+    def get_and_delete_messages(self):
156+            return self.pop(self.MESSAGES_NAME, [])
157+
158+    def create_message(self, message):
159+        messages = self.get(self.MESSAGES_NAME)
160+        if messages is None:
161+            messages = []
162+            self[self.MESSAGES_NAME] = messages
163+        messages.append(message)
164+        self.modified = True
165+
166     def encode(self, session_dict):
167         "Returns the given session dictionary pickled and encoded as a string."
168         pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL)
169Index: docs/sessions.txt
170===================================================================
171--- docs/sessions.txt   (revision 6894)
172+++ docs/sessions.txt   (working copy)
173@@ -193,6 +193,39 @@
174         request.session.set_test_cookie()
175         return render_to_response('foo/login_form.html')
176 
177+Messages
178+========
179+
180+**New in Django development version**
181+
182+The session message system provides a simple way to queue messages for all
183+(anonymous or authenticated) site visitors. To associate messages with users in
184+the user database, use the `authentication message framework`_.
185+
186+.. _authentication message framework: ../authentication/#messages
187+
188+Messages are associated with a session, therefore a message only lasts as long
189+as a session is valid (see `browser-length sessions vs. persistent sessions`_).
190+
191+The message system relies on the session middleware and is accessed via
192+``request.session``. The API is simple:
193+
194+    * To create a new message, use
195+      ``request.session.create_message(message='message text').``
196+
197+    * To retreive the messages, use ``request.session.get_messages()``,
198+      which returns a list of any messages (strings) in the session's queue.
199+
200+    * To retrieve and delete messages, use
201+      ``request.session.get_and_delete_messages()``, which returns the list of
202+      any messages in the session's queue and then deletes the messages from the
203+      queue.
204+
205+The `django.core.context_processors.messages`_ context processor makes both
206+session messages and user messages available to templates.
207+
208+.. _django.core.context_processors.messages: ../templates_python/#django-core-context_processors-messages
209+
210 Using sessions out of views
211 ===========================
212 
213Index: docs/authentication.txt
214===================================================================
215--- docs/authentication.txt     (revision 6894)
216+++ docs/authentication.txt     (working copy)
217@@ -956,8 +956,11 @@
218 Messages
219 ========
220 
221-The message system is a lightweight way to queue messages for given users.
222+The user message system is a lightweight way to queue messages for given users.
223+To send messages to anonymous users, use `session messages`_.
224 
225+.. _session framework: ../sessions/#messages
226+
227 A message is associated with a ``User``. There's no concept of expiration or
228 timestamps.
229 
230@@ -983,24 +986,20 @@
231             context_instance=RequestContext(request))
232 
233 When you use ``RequestContext``, the currently logged-in user and his/her
234-messages are made available in the `template context`_ as the template variable
235-``{{ messages }}``. Here's an example of template code that displays messages::
236+messages are made available in the `template context`_ as the ``{{ messages }}``
237+template variable.
238 
239-    {% if messages %}
240-    <ul>
241-        {% for message in messages %}
242-        <li>{{ message }}</li>
243-        {% endfor %}
244-    </ul>
245-    {% endif %}
246+**New in Django development version**
247 
248-Note that ``RequestContext`` calls ``get_and_delete_messages`` behind the
249-scenes, so any messages will be deleted even if you don't display them.
250+The ``{{ messages }}`` template variable will also contain session messages.
251+For more information, see `django.core.context_processors.messages`_.
252 
253-Finally, note that this messages framework only works with users in the user
254-database. To send messages to anonymous users, use the `session framework`_.
255+.. _django.core.context_processors.messages: ../templates_python/#django-core-context_processors-messages
256 
257-.. _session framework: ../sessions/
258+Also note that previously, ``RequestContext`` directly called
259+``get_and_delete_messages`` behind the scenes, so any messages were deleted even
260+if not displayed. Messages are now only deleted if the ``{{ messages }}``
261+variable is accessed in a template.
262 
263 Other authentication sources
264 ============================
265Index: docs/templates_python.txt
266===================================================================
267--- docs/templates_python.txt   (revision 6894)
268+++ docs/templates_python.txt   (working copy)
269@@ -295,7 +295,8 @@
270     ("django.core.context_processors.auth",
271     "django.core.context_processors.debug",
272     "django.core.context_processors.i18n",
273-    "django.core.context_processors.media")
274+    "django.core.context_processors.media",
275+    "django.core.context_processors.messages")
276 
277 Each processor is applied in order. That means, if one processor adds a
278 variable to the context and a second processor adds a variable with the same
279@@ -345,11 +346,6 @@
280       logged-in user (or an ``AnonymousUser`` instance, if the client isn't
281       logged in). See the `user authentication docs`_.
282 
283-    * ``messages`` -- A list of messages (as strings) for the currently
284-      logged-in user. Behind the scenes, this calls
285-      ``request.user.get_and_delete_messages()`` for every request. That method
286-      collects the user's messages and deletes them from the database.
287-
288       Note that messages are set with ``user.message_set.create``. See the
289       `message docs`_ for more.
290 
291@@ -358,10 +354,13 @@
292       permissions that the currently logged-in user has. See the `permissions
293       docs`_.
294 
295-.. _user authentication docs: ../authentication/#users
296-.. _message docs: ../authentication/#messages
297-.. _permissions docs: ../authentication/#permissions
298+**New in Django development version**
299 
300+Previously, a ``messages`` variable was also added to ``RequestContext``
301+containing a list of messages for the currently logged-in user. This
302+functionality has been moved to the `django.core.context_processors.messages`_
303+context processor.
304+
305 django.core.context_processors.debug
306 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
307 
308@@ -409,6 +408,31 @@
309 `HttpRequest object`_. Note that this processor is not enabled by default;
310 you'll have to activate it.
311 
312+django.core.context_processors.messages
313+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
314+
315+If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
316+``RequestContext`` will contain a variable ``messages``, which is a list of
317+messages (as strings) containing any messages for the current session (see
318+`session messages`_) or the currently logged-in user (see `auth messages`_).
319+
320+The messages are not retrieved and cleared (using ``get_and_delete_messages``)
321+until the ``messages`` variable is accessed in a template.
322+
323+Here's an example of template code that displays messages made available by this
324+context processor::
325+
326+       {% if messages %}
327+       <ul>
328+           {% for message in messages %}
329+           <li>{{ message }}</li>
330+           {% endfor %}
331+       </ul>
332+       {% endif %}
333+
334+.. _session messages: ../sessions/#messages
335+.. _auth messages: ../authentication/#messages
336+
337 Writing your own context processors
338 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
339