base.py 6.1 KB

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