docker setup
This commit is contained in:
		| @ -0,0 +1,2 @@ | ||||
| from django.contrib.messages.api import *  # NOQA | ||||
| from django.contrib.messages.constants import *  # NOQA | ||||
| @ -0,0 +1,127 @@ | ||||
| from django.contrib.messages import constants | ||||
| from django.contrib.messages.storage import default_storage | ||||
|  | ||||
| __all__ = ( | ||||
|     "add_message", | ||||
|     "get_messages", | ||||
|     "get_level", | ||||
|     "set_level", | ||||
|     "debug", | ||||
|     "info", | ||||
|     "success", | ||||
|     "warning", | ||||
|     "error", | ||||
|     "MessageFailure", | ||||
| ) | ||||
|  | ||||
|  | ||||
| class MessageFailure(Exception): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| def add_message(request, level, message, extra_tags="", fail_silently=False): | ||||
|     """ | ||||
|     Attempt to add a message to the request using the 'messages' app. | ||||
|     """ | ||||
|     try: | ||||
|         messages = request._messages | ||||
|     except AttributeError: | ||||
|         if not hasattr(request, "META"): | ||||
|             raise TypeError( | ||||
|                 "add_message() argument must be an HttpRequest object, not " | ||||
|                 "'%s'." % request.__class__.__name__ | ||||
|             ) | ||||
|         if not fail_silently: | ||||
|             raise MessageFailure( | ||||
|                 "You cannot add messages without installing " | ||||
|                 "django.contrib.messages.middleware.MessageMiddleware" | ||||
|             ) | ||||
|     else: | ||||
|         return messages.add(level, message, extra_tags) | ||||
|  | ||||
|  | ||||
| def get_messages(request): | ||||
|     """ | ||||
|     Return the message storage on the request if it exists, otherwise return | ||||
|     an empty list. | ||||
|     """ | ||||
|     return getattr(request, "_messages", []) | ||||
|  | ||||
|  | ||||
| def get_level(request): | ||||
|     """ | ||||
|     Return the minimum level of messages to be recorded. | ||||
|  | ||||
|     The default level is the ``MESSAGE_LEVEL`` setting. If this is not found, | ||||
|     use the ``INFO`` level. | ||||
|     """ | ||||
|     storage = getattr(request, "_messages", default_storage(request)) | ||||
|     return storage.level | ||||
|  | ||||
|  | ||||
| def set_level(request, level): | ||||
|     """ | ||||
|     Set the minimum level of messages to be recorded, and return ``True`` if | ||||
|     the level was recorded successfully. | ||||
|  | ||||
|     If set to ``None``, use the default level (see the get_level() function). | ||||
|     """ | ||||
|     if not hasattr(request, "_messages"): | ||||
|         return False | ||||
|     request._messages.level = level | ||||
|     return True | ||||
|  | ||||
|  | ||||
| def debug(request, message, extra_tags="", fail_silently=False): | ||||
|     """Add a message with the ``DEBUG`` level.""" | ||||
|     add_message( | ||||
|         request, | ||||
|         constants.DEBUG, | ||||
|         message, | ||||
|         extra_tags=extra_tags, | ||||
|         fail_silently=fail_silently, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def info(request, message, extra_tags="", fail_silently=False): | ||||
|     """Add a message with the ``INFO`` level.""" | ||||
|     add_message( | ||||
|         request, | ||||
|         constants.INFO, | ||||
|         message, | ||||
|         extra_tags=extra_tags, | ||||
|         fail_silently=fail_silently, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def success(request, message, extra_tags="", fail_silently=False): | ||||
|     """Add a message with the ``SUCCESS`` level.""" | ||||
|     add_message( | ||||
|         request, | ||||
|         constants.SUCCESS, | ||||
|         message, | ||||
|         extra_tags=extra_tags, | ||||
|         fail_silently=fail_silently, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def warning(request, message, extra_tags="", fail_silently=False): | ||||
|     """Add a message with the ``WARNING`` level.""" | ||||
|     add_message( | ||||
|         request, | ||||
|         constants.WARNING, | ||||
|         message, | ||||
|         extra_tags=extra_tags, | ||||
|         fail_silently=fail_silently, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def error(request, message, extra_tags="", fail_silently=False): | ||||
|     """Add a message with the ``ERROR`` level.""" | ||||
|     add_message( | ||||
|         request, | ||||
|         constants.ERROR, | ||||
|         message, | ||||
|         extra_tags=extra_tags, | ||||
|         fail_silently=fail_silently, | ||||
|     ) | ||||
| @ -0,0 +1,18 @@ | ||||
| from django.apps import AppConfig | ||||
| from django.contrib.messages.storage import base | ||||
| from django.contrib.messages.utils import get_level_tags | ||||
| from django.core.signals import setting_changed | ||||
| from django.utils.translation import gettext_lazy as _ | ||||
|  | ||||
|  | ||||
| def update_level_tags(setting, **kwargs): | ||||
|     if setting == "MESSAGE_TAGS": | ||||
|         base.LEVEL_TAGS = get_level_tags() | ||||
|  | ||||
|  | ||||
| class MessagesConfig(AppConfig): | ||||
|     name = "django.contrib.messages" | ||||
|     verbose_name = _("Messages") | ||||
|  | ||||
|     def ready(self): | ||||
|         setting_changed.connect(update_level_tags) | ||||
| @ -0,0 +1,21 @@ | ||||
| DEBUG = 10 | ||||
| INFO = 20 | ||||
| SUCCESS = 25 | ||||
| WARNING = 30 | ||||
| ERROR = 40 | ||||
|  | ||||
| DEFAULT_TAGS = { | ||||
|     DEBUG: "debug", | ||||
|     INFO: "info", | ||||
|     SUCCESS: "success", | ||||
|     WARNING: "warning", | ||||
|     ERROR: "error", | ||||
| } | ||||
|  | ||||
| DEFAULT_LEVELS = { | ||||
|     "DEBUG": DEBUG, | ||||
|     "INFO": INFO, | ||||
|     "SUCCESS": SUCCESS, | ||||
|     "WARNING": WARNING, | ||||
|     "ERROR": ERROR, | ||||
| } | ||||
| @ -0,0 +1,13 @@ | ||||
| from django.contrib.messages.api import get_messages | ||||
| from django.contrib.messages.constants import DEFAULT_LEVELS | ||||
|  | ||||
|  | ||||
| def messages(request): | ||||
|     """ | ||||
|     Return a lazy 'messages' context variable as well as | ||||
|     'DEFAULT_MESSAGE_LEVELS'. | ||||
|     """ | ||||
|     return { | ||||
|         "messages": get_messages(request), | ||||
|         "DEFAULT_MESSAGE_LEVELS": DEFAULT_LEVELS, | ||||
|     } | ||||
| @ -0,0 +1,26 @@ | ||||
| from django.conf import settings | ||||
| from django.contrib.messages.storage import default_storage | ||||
| from django.utils.deprecation import MiddlewareMixin | ||||
|  | ||||
|  | ||||
| class MessageMiddleware(MiddlewareMixin): | ||||
|     """ | ||||
|     Middleware that handles temporary messages. | ||||
|     """ | ||||
|  | ||||
|     def process_request(self, request): | ||||
|         request._messages = default_storage(request) | ||||
|  | ||||
|     def process_response(self, request, response): | ||||
|         """ | ||||
|         Update the storage backend (i.e., save the messages). | ||||
|  | ||||
|         Raise ValueError if not all messages could be stored and DEBUG is True. | ||||
|         """ | ||||
|         # A higher middleware layer may return a request which does not contain | ||||
|         # messages storage, so make no assumption that it will be there. | ||||
|         if hasattr(request, "_messages"): | ||||
|             unstored_messages = request._messages.update(response) | ||||
|             if unstored_messages and settings.DEBUG: | ||||
|                 raise ValueError("Not all temporary messages could be stored.") | ||||
|         return response | ||||
| @ -0,0 +1,12 @@ | ||||
| from django.conf import settings | ||||
| from django.utils.module_loading import import_string | ||||
|  | ||||
|  | ||||
| def default_storage(request): | ||||
|     """ | ||||
|     Callable with the same interface as the storage classes. | ||||
|  | ||||
|     This isn't just default_storage = import_string(settings.MESSAGE_STORAGE) | ||||
|     to avoid accessing the settings at the module level. | ||||
|     """ | ||||
|     return import_string(settings.MESSAGE_STORAGE)(request) | ||||
| @ -0,0 +1,178 @@ | ||||
| from django.conf import settings | ||||
| from django.contrib.messages import constants, utils | ||||
|  | ||||
| LEVEL_TAGS = utils.get_level_tags() | ||||
|  | ||||
|  | ||||
| class Message: | ||||
|     """ | ||||
|     Represent an actual message that can be stored in any of the supported | ||||
|     storage classes (typically session- or cookie-based) and rendered in a view | ||||
|     or template. | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, level, message, extra_tags=None): | ||||
|         self.level = int(level) | ||||
|         self.message = message | ||||
|         self.extra_tags = extra_tags | ||||
|  | ||||
|     def _prepare(self): | ||||
|         """ | ||||
|         Prepare the message for serialization by forcing the ``message`` | ||||
|         and ``extra_tags`` to str in case they are lazy translations. | ||||
|         """ | ||||
|         self.message = str(self.message) | ||||
|         self.extra_tags = str(self.extra_tags) if self.extra_tags is not None else None | ||||
|  | ||||
|     def __eq__(self, other): | ||||
|         if not isinstance(other, Message): | ||||
|             return NotImplemented | ||||
|         return self.level == other.level and self.message == other.message | ||||
|  | ||||
|     def __str__(self): | ||||
|         return str(self.message) | ||||
|  | ||||
|     @property | ||||
|     def tags(self): | ||||
|         return " ".join(tag for tag in [self.extra_tags, self.level_tag] if tag) | ||||
|  | ||||
|     @property | ||||
|     def level_tag(self): | ||||
|         return LEVEL_TAGS.get(self.level, "") | ||||
|  | ||||
|  | ||||
| class BaseStorage: | ||||
|     """ | ||||
|     This is the base backend for temporary message storage. | ||||
|  | ||||
|     This is not a complete class; to be a usable storage backend, it must be | ||||
|     subclassed and the two methods ``_get`` and ``_store`` overridden. | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, request, *args, **kwargs): | ||||
|         self.request = request | ||||
|         self._queued_messages = [] | ||||
|         self.used = False | ||||
|         self.added_new = False | ||||
|         super().__init__(*args, **kwargs) | ||||
|  | ||||
|     def __len__(self): | ||||
|         return len(self._loaded_messages) + len(self._queued_messages) | ||||
|  | ||||
|     def __iter__(self): | ||||
|         self.used = True | ||||
|         if self._queued_messages: | ||||
|             self._loaded_messages.extend(self._queued_messages) | ||||
|             self._queued_messages = [] | ||||
|         return iter(self._loaded_messages) | ||||
|  | ||||
|     def __contains__(self, item): | ||||
|         return item in self._loaded_messages or item in self._queued_messages | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return f"<{self.__class__.__qualname__}: request={self.request!r}>" | ||||
|  | ||||
|     @property | ||||
|     def _loaded_messages(self): | ||||
|         """ | ||||
|         Return a list of loaded messages, retrieving them first if they have | ||||
|         not been loaded yet. | ||||
|         """ | ||||
|         if not hasattr(self, "_loaded_data"): | ||||
|             messages, all_retrieved = self._get() | ||||
|             self._loaded_data = messages or [] | ||||
|         return self._loaded_data | ||||
|  | ||||
|     def _get(self, *args, **kwargs): | ||||
|         """ | ||||
|         Retrieve a list of stored messages. Return a tuple of the messages | ||||
|         and a flag indicating whether or not all the messages originally | ||||
|         intended to be stored in this storage were, in fact, stored and | ||||
|         retrieved; e.g., ``(messages, all_retrieved)``. | ||||
|  | ||||
|         **This method must be implemented by a subclass.** | ||||
|  | ||||
|         If it is possible to tell if the backend was not used (as opposed to | ||||
|         just containing no messages) then ``None`` should be returned in | ||||
|         place of ``messages``. | ||||
|         """ | ||||
|         raise NotImplementedError( | ||||
|             "subclasses of BaseStorage must provide a _get() method" | ||||
|         ) | ||||
|  | ||||
|     def _store(self, messages, response, *args, **kwargs): | ||||
|         """ | ||||
|         Store a list of messages and return a list of any messages which could | ||||
|         not be stored. | ||||
|  | ||||
|         One type of object must be able to be stored, ``Message``. | ||||
|  | ||||
|         **This method must be implemented by a subclass.** | ||||
|         """ | ||||
|         raise NotImplementedError( | ||||
|             "subclasses of BaseStorage must provide a _store() method" | ||||
|         ) | ||||
|  | ||||
|     def _prepare_messages(self, messages): | ||||
|         """ | ||||
|         Prepare a list of messages for storage. | ||||
|         """ | ||||
|         for message in messages: | ||||
|             message._prepare() | ||||
|  | ||||
|     def update(self, response): | ||||
|         """ | ||||
|         Store all unread messages. | ||||
|  | ||||
|         If the backend has yet to be iterated, store previously stored messages | ||||
|         again. Otherwise, only store messages added after the last iteration. | ||||
|         """ | ||||
|         self._prepare_messages(self._queued_messages) | ||||
|         if self.used: | ||||
|             return self._store(self._queued_messages, response) | ||||
|         elif self.added_new: | ||||
|             messages = self._loaded_messages + self._queued_messages | ||||
|             return self._store(messages, response) | ||||
|  | ||||
|     def add(self, level, message, extra_tags=""): | ||||
|         """ | ||||
|         Queue a message to be stored. | ||||
|  | ||||
|         The message is only queued if it contained something and its level is | ||||
|         not less than the recording level (``self.level``). | ||||
|         """ | ||||
|         if not message: | ||||
|             return | ||||
|         # Check that the message level is not less than the recording level. | ||||
|         level = int(level) | ||||
|         if level < self.level: | ||||
|             return | ||||
|         # Add the message. | ||||
|         self.added_new = True | ||||
|         message = Message(level, message, extra_tags=extra_tags) | ||||
|         self._queued_messages.append(message) | ||||
|  | ||||
|     def _get_level(self): | ||||
|         """ | ||||
|         Return the minimum recorded level. | ||||
|  | ||||
|         The default level is the ``MESSAGE_LEVEL`` setting. If this is | ||||
|         not found, the ``INFO`` level is used. | ||||
|         """ | ||||
|         if not hasattr(self, "_level"): | ||||
|             self._level = getattr(settings, "MESSAGE_LEVEL", constants.INFO) | ||||
|         return self._level | ||||
|  | ||||
|     def _set_level(self, value=None): | ||||
|         """ | ||||
|         Set a custom minimum recorded level. | ||||
|  | ||||
|         If set to ``None``, the default level will be used (see the | ||||
|         ``_get_level`` method). | ||||
|         """ | ||||
|         if value is None and hasattr(self, "_level"): | ||||
|             del self._level | ||||
|         else: | ||||
|             self._level = int(value) | ||||
|  | ||||
|     level = property(_get_level, _set_level, _set_level) | ||||
| @ -0,0 +1,174 @@ | ||||
| import binascii | ||||
| import json | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.contrib.messages.storage.base import BaseStorage, Message | ||||
| from django.core import signing | ||||
| from django.http import SimpleCookie | ||||
| from django.utils.safestring import SafeData, mark_safe | ||||
|  | ||||
|  | ||||
| class MessageEncoder(json.JSONEncoder): | ||||
|     """ | ||||
|     Compactly serialize instances of the ``Message`` class as JSON. | ||||
|     """ | ||||
|  | ||||
|     message_key = "__json_message" | ||||
|  | ||||
|     def default(self, obj): | ||||
|         if isinstance(obj, Message): | ||||
|             # Using 0/1 here instead of False/True to produce more compact json | ||||
|             is_safedata = 1 if isinstance(obj.message, SafeData) else 0 | ||||
|             message = [self.message_key, is_safedata, obj.level, obj.message] | ||||
|             if obj.extra_tags is not None: | ||||
|                 message.append(obj.extra_tags) | ||||
|             return message | ||||
|         return super().default(obj) | ||||
|  | ||||
|  | ||||
| class MessageDecoder(json.JSONDecoder): | ||||
|     """ | ||||
|     Decode JSON that includes serialized ``Message`` instances. | ||||
|     """ | ||||
|  | ||||
|     def process_messages(self, obj): | ||||
|         if isinstance(obj, list) and obj: | ||||
|             if obj[0] == MessageEncoder.message_key: | ||||
|                 if obj[1]: | ||||
|                     obj[3] = mark_safe(obj[3]) | ||||
|                 return Message(*obj[2:]) | ||||
|             return [self.process_messages(item) for item in obj] | ||||
|         if isinstance(obj, dict): | ||||
|             return {key: self.process_messages(value) for key, value in obj.items()} | ||||
|         return obj | ||||
|  | ||||
|     def decode(self, s, **kwargs): | ||||
|         decoded = super().decode(s, **kwargs) | ||||
|         return self.process_messages(decoded) | ||||
|  | ||||
|  | ||||
| class MessageSerializer: | ||||
|     def dumps(self, obj): | ||||
|         return json.dumps( | ||||
|             obj, | ||||
|             separators=(",", ":"), | ||||
|             cls=MessageEncoder, | ||||
|         ).encode("latin-1") | ||||
|  | ||||
|     def loads(self, data): | ||||
|         return json.loads(data.decode("latin-1"), cls=MessageDecoder) | ||||
|  | ||||
|  | ||||
| class CookieStorage(BaseStorage): | ||||
|     """ | ||||
|     Store messages in a cookie. | ||||
|     """ | ||||
|  | ||||
|     cookie_name = "messages" | ||||
|     # uwsgi's default configuration enforces a maximum size of 4kb for all the | ||||
|     # HTTP headers. In order to leave some room for other cookies and headers, | ||||
|     # restrict the session cookie to 1/2 of 4kb. See #18781. | ||||
|     max_cookie_size = 2048 | ||||
|     not_finished = "__messagesnotfinished__" | ||||
|     key_salt = "django.contrib.messages" | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         super().__init__(*args, **kwargs) | ||||
|         self.signer = signing.get_cookie_signer(salt=self.key_salt) | ||||
|  | ||||
|     def _get(self, *args, **kwargs): | ||||
|         """ | ||||
|         Retrieve a list of messages from the messages cookie. If the | ||||
|         not_finished sentinel value is found at the end of the message list, | ||||
|         remove it and return a result indicating that not all messages were | ||||
|         retrieved by this storage. | ||||
|         """ | ||||
|         data = self.request.COOKIES.get(self.cookie_name) | ||||
|         messages = self._decode(data) | ||||
|         all_retrieved = not (messages and messages[-1] == self.not_finished) | ||||
|         if messages and not all_retrieved: | ||||
|             # remove the sentinel value | ||||
|             messages.pop() | ||||
|         return messages, all_retrieved | ||||
|  | ||||
|     def _update_cookie(self, encoded_data, response): | ||||
|         """ | ||||
|         Either set the cookie with the encoded data if there is any data to | ||||
|         store, or delete the cookie. | ||||
|         """ | ||||
|         if encoded_data: | ||||
|             response.set_cookie( | ||||
|                 self.cookie_name, | ||||
|                 encoded_data, | ||||
|                 domain=settings.SESSION_COOKIE_DOMAIN, | ||||
|                 secure=settings.SESSION_COOKIE_SECURE or None, | ||||
|                 httponly=settings.SESSION_COOKIE_HTTPONLY or None, | ||||
|                 samesite=settings.SESSION_COOKIE_SAMESITE, | ||||
|             ) | ||||
|         else: | ||||
|             response.delete_cookie( | ||||
|                 self.cookie_name, | ||||
|                 domain=settings.SESSION_COOKIE_DOMAIN, | ||||
|                 samesite=settings.SESSION_COOKIE_SAMESITE, | ||||
|             ) | ||||
|  | ||||
|     def _store(self, messages, response, remove_oldest=True, *args, **kwargs): | ||||
|         """ | ||||
|         Store the messages to a cookie and return a list of any messages which | ||||
|         could not be stored. | ||||
|  | ||||
|         If the encoded data is larger than ``max_cookie_size``, remove | ||||
|         messages until the data fits (these are the messages which are | ||||
|         returned), and add the not_finished sentinel value to indicate as much. | ||||
|         """ | ||||
|         unstored_messages = [] | ||||
|         encoded_data = self._encode(messages) | ||||
|         if self.max_cookie_size: | ||||
|             # data is going to be stored eventually by SimpleCookie, which | ||||
|             # adds its own overhead, which we must account for. | ||||
|             cookie = SimpleCookie()  # create outside the loop | ||||
|  | ||||
|             def stored_length(val): | ||||
|                 return len(cookie.value_encode(val)[1]) | ||||
|  | ||||
|             while encoded_data and stored_length(encoded_data) > self.max_cookie_size: | ||||
|                 if remove_oldest: | ||||
|                     unstored_messages.append(messages.pop(0)) | ||||
|                 else: | ||||
|                     unstored_messages.insert(0, messages.pop()) | ||||
|                 encoded_data = self._encode( | ||||
|                     messages + [self.not_finished], encode_empty=unstored_messages | ||||
|                 ) | ||||
|         self._update_cookie(encoded_data, response) | ||||
|         return unstored_messages | ||||
|  | ||||
|     def _encode(self, messages, encode_empty=False): | ||||
|         """ | ||||
|         Return an encoded version of the messages list which can be stored as | ||||
|         plain text. | ||||
|  | ||||
|         Since the data will be retrieved from the client-side, the encoded data | ||||
|         also contains a hash to ensure that the data was not tampered with. | ||||
|         """ | ||||
|         if messages or encode_empty: | ||||
|             return self.signer.sign_object( | ||||
|                 messages, serializer=MessageSerializer, compress=True | ||||
|             ) | ||||
|  | ||||
|     def _decode(self, data): | ||||
|         """ | ||||
|         Safely decode an encoded text stream back into a list of messages. | ||||
|  | ||||
|         If the encoded text stream contained an invalid hash or was in an | ||||
|         invalid format, return None. | ||||
|         """ | ||||
|         if not data: | ||||
|             return None | ||||
|         try: | ||||
|             return self.signer.unsign_object(data, serializer=MessageSerializer) | ||||
|         except (signing.BadSignature, binascii.Error, json.JSONDecodeError): | ||||
|             pass | ||||
|         # Mark the data as used (so it gets removed) since something was wrong | ||||
|         # with the data. | ||||
|         self.used = True | ||||
|         return None | ||||
| @ -0,0 +1,56 @@ | ||||
| from django.contrib.messages.storage.base import BaseStorage | ||||
| from django.contrib.messages.storage.cookie import CookieStorage | ||||
| from django.contrib.messages.storage.session import SessionStorage | ||||
|  | ||||
|  | ||||
| class FallbackStorage(BaseStorage): | ||||
|     """ | ||||
|     Try to store all messages in the first backend. Store any unstored | ||||
|     messages in each subsequent backend. | ||||
|     """ | ||||
|  | ||||
|     storage_classes = (CookieStorage, SessionStorage) | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         super().__init__(*args, **kwargs) | ||||
|         self.storages = [ | ||||
|             storage_class(*args, **kwargs) for storage_class in self.storage_classes | ||||
|         ] | ||||
|         self._used_storages = set() | ||||
|  | ||||
|     def _get(self, *args, **kwargs): | ||||
|         """ | ||||
|         Get a single list of messages from all storage backends. | ||||
|         """ | ||||
|         all_messages = [] | ||||
|         for storage in self.storages: | ||||
|             messages, all_retrieved = storage._get() | ||||
|             # If the backend hasn't been used, no more retrieval is necessary. | ||||
|             if messages is None: | ||||
|                 break | ||||
|             if messages: | ||||
|                 self._used_storages.add(storage) | ||||
|             all_messages.extend(messages) | ||||
|             # If this storage class contained all the messages, no further | ||||
|             # retrieval is necessary | ||||
|             if all_retrieved: | ||||
|                 break | ||||
|         return all_messages, all_retrieved | ||||
|  | ||||
|     def _store(self, messages, response, *args, **kwargs): | ||||
|         """ | ||||
|         Store the messages and return any unstored messages after trying all | ||||
|         backends. | ||||
|  | ||||
|         For each storage backend, any messages not stored are passed on to the | ||||
|         next backend. | ||||
|         """ | ||||
|         for storage in self.storages: | ||||
|             if messages: | ||||
|                 messages = storage._store(messages, response, remove_oldest=False) | ||||
|             # Even if there are no more messages, continue iterating to ensure | ||||
|             # storages which contained messages are flushed. | ||||
|             elif storage in self._used_storages: | ||||
|                 storage._store([], response) | ||||
|                 self._used_storages.remove(storage) | ||||
|         return messages | ||||
| @ -0,0 +1,52 @@ | ||||
| import json | ||||
|  | ||||
| from django.contrib.messages.storage.base import BaseStorage | ||||
| from django.contrib.messages.storage.cookie import MessageDecoder, MessageEncoder | ||||
| from django.core.exceptions import ImproperlyConfigured | ||||
|  | ||||
|  | ||||
| class SessionStorage(BaseStorage): | ||||
|     """ | ||||
|     Store messages in the session (that is, django.contrib.sessions). | ||||
|     """ | ||||
|  | ||||
|     session_key = "_messages" | ||||
|  | ||||
|     def __init__(self, request, *args, **kwargs): | ||||
|         if not hasattr(request, "session"): | ||||
|             raise ImproperlyConfigured( | ||||
|                 "The session-based temporary message storage requires session " | ||||
|                 "middleware to be installed, and come before the message " | ||||
|                 "middleware in the MIDDLEWARE list." | ||||
|             ) | ||||
|         super().__init__(request, *args, **kwargs) | ||||
|  | ||||
|     def _get(self, *args, **kwargs): | ||||
|         """ | ||||
|         Retrieve a list of messages from the request's session. This storage | ||||
|         always stores everything it is given, so return True for the | ||||
|         all_retrieved flag. | ||||
|         """ | ||||
|         return ( | ||||
|             self.deserialize_messages(self.request.session.get(self.session_key)), | ||||
|             True, | ||||
|         ) | ||||
|  | ||||
|     def _store(self, messages, response, *args, **kwargs): | ||||
|         """ | ||||
|         Store a list of messages to the request's session. | ||||
|         """ | ||||
|         if messages: | ||||
|             self.request.session[self.session_key] = self.serialize_messages(messages) | ||||
|         else: | ||||
|             self.request.session.pop(self.session_key, None) | ||||
|         return [] | ||||
|  | ||||
|     def serialize_messages(self, messages): | ||||
|         encoder = MessageEncoder() | ||||
|         return encoder.encode(messages) | ||||
|  | ||||
|     def deserialize_messages(self, data): | ||||
|         if data and isinstance(data, str): | ||||
|             return json.loads(data, cls=MessageDecoder) | ||||
|         return data | ||||
| @ -0,0 +1,12 @@ | ||||
| from django.conf import settings | ||||
| from django.contrib.messages import constants | ||||
|  | ||||
|  | ||||
| def get_level_tags(): | ||||
|     """ | ||||
|     Return the message level tags. | ||||
|     """ | ||||
|     return { | ||||
|         **constants.DEFAULT_TAGS, | ||||
|         **getattr(settings, "MESSAGE_TAGS", {}), | ||||
|     } | ||||
| @ -0,0 +1,19 @@ | ||||
| from django.contrib import messages | ||||
|  | ||||
|  | ||||
| class SuccessMessageMixin: | ||||
|     """ | ||||
|     Add a success message on successful form submission. | ||||
|     """ | ||||
|  | ||||
|     success_message = "" | ||||
|  | ||||
|     def form_valid(self, form): | ||||
|         response = super().form_valid(form) | ||||
|         success_message = self.get_success_message(form.cleaned_data) | ||||
|         if success_message: | ||||
|             messages.success(self.request, success_message) | ||||
|         return response | ||||
|  | ||||
|     def get_success_message(self, cleaned_data): | ||||
|         return self.success_message % cleaned_data | ||||
		Reference in New Issue
	
	Block a user