Code

Ticket #4604: messages.4.diff

File messages.4.diff, 15.1 KB (added by __hawkeye__, 6 years ago)

Slight documentation change.

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