|
1 from django.conf import settings |
|
2 from django.utils.encoding import force_unicode, StrAndUnicode |
|
3 from django.contrib.messages import constants, utils |
|
4 |
|
5 |
|
6 LEVEL_TAGS = utils.get_level_tags() |
|
7 |
|
8 |
|
9 class Message(StrAndUnicode): |
|
10 """ |
|
11 Represents an actual message that can be stored in any of the supported |
|
12 storage classes (typically session- or cookie-based) and rendered in a view |
|
13 or template. |
|
14 """ |
|
15 |
|
16 def __init__(self, level, message, extra_tags=None): |
|
17 self.level = int(level) |
|
18 self.message = message |
|
19 self.extra_tags = extra_tags |
|
20 |
|
21 def _prepare(self): |
|
22 """ |
|
23 Prepares the message for serialization by forcing the ``message`` |
|
24 and ``extra_tags`` to unicode in case they are lazy translations. |
|
25 |
|
26 Known "safe" types (None, int, etc.) are not converted (see Django's |
|
27 ``force_unicode`` implementation for details). |
|
28 """ |
|
29 self.message = force_unicode(self.message, strings_only=True) |
|
30 self.extra_tags = force_unicode(self.extra_tags, strings_only=True) |
|
31 |
|
32 def __eq__(self, other): |
|
33 return isinstance(other, Message) and self.level == other.level and \ |
|
34 self.message == other.message |
|
35 |
|
36 def __unicode__(self): |
|
37 return force_unicode(self.message) |
|
38 |
|
39 def _get_tags(self): |
|
40 label_tag = force_unicode(LEVEL_TAGS.get(self.level, ''), |
|
41 strings_only=True) |
|
42 extra_tags = force_unicode(self.extra_tags, strings_only=True) |
|
43 if extra_tags and label_tag: |
|
44 return u' '.join([extra_tags, label_tag]) |
|
45 elif extra_tags: |
|
46 return extra_tags |
|
47 elif label_tag: |
|
48 return label_tag |
|
49 return '' |
|
50 tags = property(_get_tags) |
|
51 |
|
52 |
|
53 class BaseStorage(object): |
|
54 """ |
|
55 This is the base backend for temporary message storage. |
|
56 |
|
57 This is not a complete class; to be a usable storage backend, it must be |
|
58 subclassed and the two methods ``_get`` and ``_store`` overridden. |
|
59 """ |
|
60 |
|
61 def __init__(self, request, *args, **kwargs): |
|
62 self.request = request |
|
63 self._queued_messages = [] |
|
64 self.used = False |
|
65 self.added_new = False |
|
66 super(BaseStorage, self).__init__(*args, **kwargs) |
|
67 |
|
68 def __len__(self): |
|
69 return len(self._loaded_messages) + len(self._queued_messages) |
|
70 |
|
71 def __iter__(self): |
|
72 self.used = True |
|
73 if self._queued_messages: |
|
74 self._loaded_messages.extend(self._queued_messages) |
|
75 self._queued_messages = [] |
|
76 return iter(self._loaded_messages) |
|
77 |
|
78 def __contains__(self, item): |
|
79 return item in self._loaded_messages or item in self._queued_messages |
|
80 |
|
81 @property |
|
82 def _loaded_messages(self): |
|
83 """ |
|
84 Returns a list of loaded messages, retrieving them first if they have |
|
85 not been loaded yet. |
|
86 """ |
|
87 if not hasattr(self, '_loaded_data'): |
|
88 messages, all_retrieved = self._get() |
|
89 self._loaded_data = messages or [] |
|
90 return self._loaded_data |
|
91 |
|
92 def _get(self, *args, **kwargs): |
|
93 """ |
|
94 Retrieves a list of stored messages. Returns a tuple of the messages |
|
95 and a flag indicating whether or not all the messages originally |
|
96 intended to be stored in this storage were, in fact, stored and |
|
97 retrieved; e.g., ``(messages, all_retrieved)``. |
|
98 |
|
99 **This method must be implemented by a subclass.** |
|
100 |
|
101 If it is possible to tell if the backend was not used (as opposed to |
|
102 just containing no messages) then ``None`` should be returned in |
|
103 place of ``messages``. |
|
104 """ |
|
105 raise NotImplementedError() |
|
106 |
|
107 def _store(self, messages, response, *args, **kwargs): |
|
108 """ |
|
109 Stores a list of messages, returning a list of any messages which could |
|
110 not be stored. |
|
111 |
|
112 One type of object must be able to be stored, ``Message``. |
|
113 |
|
114 **This method must be implemented by a subclass.** |
|
115 """ |
|
116 raise NotImplementedError() |
|
117 |
|
118 def _prepare_messages(self, messages): |
|
119 """ |
|
120 Prepares a list of messages for storage. |
|
121 """ |
|
122 for message in messages: |
|
123 message._prepare() |
|
124 |
|
125 def update(self, response): |
|
126 """ |
|
127 Stores all unread messages. |
|
128 |
|
129 If the backend has yet to be iterated, previously stored messages will |
|
130 be stored again. Otherwise, only messages added after the last |
|
131 iteration will be stored. |
|
132 """ |
|
133 self._prepare_messages(self._queued_messages) |
|
134 if self.used: |
|
135 return self._store(self._queued_messages, response) |
|
136 elif self.added_new: |
|
137 messages = self._loaded_messages + self._queued_messages |
|
138 return self._store(messages, response) |
|
139 |
|
140 def add(self, level, message, extra_tags=''): |
|
141 """ |
|
142 Queues a message to be stored. |
|
143 |
|
144 The message is only queued if it contained something and its level is |
|
145 not less than the recording level (``self.level``). |
|
146 """ |
|
147 if not message: |
|
148 return |
|
149 # Check that the message level is not less than the recording level. |
|
150 level = int(level) |
|
151 if level < self.level: |
|
152 return |
|
153 # Add the message. |
|
154 self.added_new = True |
|
155 message = Message(level, message, extra_tags=extra_tags) |
|
156 self._queued_messages.append(message) |
|
157 |
|
158 def _get_level(self): |
|
159 """ |
|
160 Returns the minimum recorded level. |
|
161 |
|
162 The default level is the ``MESSAGE_LEVEL`` setting. If this is |
|
163 not found, the ``INFO`` level is used. |
|
164 """ |
|
165 if not hasattr(self, '_level'): |
|
166 self._level = getattr(settings, 'MESSAGE_LEVEL', constants.INFO) |
|
167 return self._level |
|
168 |
|
169 def _set_level(self, value=None): |
|
170 """ |
|
171 Sets a custom minimum recorded level. |
|
172 |
|
173 If set to ``None``, the default level will be used (see the |
|
174 ``_get_level`` method). |
|
175 """ |
|
176 if value is None and hasattr(self, '_level'): |
|
177 del self._level |
|
178 else: |
|
179 self._level = int(value) |
|
180 |
|
181 level = property(_get_level, _set_level, _set_level) |