Django

Code

Ticket #4604: django-contrib-messages-9f54c0f8719c.diff

File django-contrib-messages-9f54c0f8719c.diff, 81.8 kB (added by tobias, 4 months ago)

diff showing changes in django-contrib-messages branch as of changeset 9f54c0f8719c

  • a/django/conf/global_settings.py

    old new  
    172172    'django.core.context_processors.i18n', 
    173173    'django.core.context_processors.media', 
    174174#    'django.core.context_processors.request', 
     175    'django.contrib.messages.context_processors.messages', 
    175176) 
    176177 
    177178# Output to use in template system for invalid (e.g. misspelled) variables. 
     
    308309    'django.contrib.sessions.middleware.SessionMiddleware', 
    309310    'django.middleware.csrf.CsrfViewMiddleware', 
    310311    'django.contrib.auth.middleware.AuthenticationMiddleware', 
     312    'django.contrib.messages.middleware.MessageMiddleware', 
    311313#     'django.middleware.http.ConditionalGetMiddleware', 
    312314#     'django.middleware.gzip.GZipMiddleware', 
    313315) 
  • a/django/conf/project_template/settings.py

    old new  
    6262    'django.contrib.sessions.middleware.SessionMiddleware', 
    6363    'django.middleware.csrf.CsrfViewMiddleware', 
    6464    'django.contrib.auth.middleware.AuthenticationMiddleware', 
     65    'django.contrib.messages.middleware.MessageMiddleware', 
    6566) 
    6667 
    6768ROOT_URLCONF = '{{ project_name }}.urls' 
     
    7778    'django.contrib.contenttypes', 
    7879    'django.contrib.sessions', 
    7980    'django.contrib.sites', 
     81    'django.contrib.messages', 
    8082) 
  • a/django/contrib/admin/options.py

    old new  
    66from django.contrib.admin import widgets 
    77from django.contrib.admin import helpers 
    88from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict 
     9from django.contrib import messages 
     10from django.contrib.messages.compat import compat_add_message 
    911from django.views.decorators.csrf import csrf_protect 
    1012from django.core.exceptions import PermissionDenied 
    1113from django.db import models, transaction 
     
    541543    def message_user(self, request, message): 
    542544        """ 
    543545        Send a message to the user. The default implementation 
    544         posts a message using the auth Message object
     546        posts a message using the django.contrib.messages backend
    545547        """ 
    546         request.user.message_set.create(message=message) 
     548        compat_add_message(request, messages.INFO, message) 
    547549 
    548550    def save_form(self, request, form, change): 
    549551        """ 
  • a/django/contrib/admin/views/template.py

    old new  
    66from django.conf import settings 
    77from django.utils.importlib import import_module 
    88from django.utils.translation import ugettext_lazy as _ 
     9from django.contrib import messages 
     10from django.contrib.messages.compat import compat_add_message 
    911 
    1012 
    1113def template_validator(request): 
     
    2325        form = TemplateValidatorForm(settings_modules, site_list, 
    2426                                     data=request.POST) 
    2527        if form.is_valid(): 
    26             request.user.message_set.create(message='The template is valid.') 
     28            compat_add_message(request, messages.INFO, 'The template is valid.') 
    2729    else: 
    2830        form = TemplateValidatorForm(settings_modules, site_list) 
    2931    return render_to_response('admin/template_validator.html', { 
  • a/django/contrib/auth/admin.py

    old new  
    33from django.contrib import admin 
    44from django.contrib.auth.forms import UserCreationForm, UserChangeForm, AdminPasswordChangeForm 
    55from django.contrib.auth.models import User, Group 
     6from django.contrib import messages 
     7from django.contrib.messages.compat import compat_add_message 
    68from django.core.exceptions import PermissionDenied 
    79from django.http import HttpResponseRedirect, Http404 
    810from django.shortcuts import render_to_response, get_object_or_404 
     
    6769                msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': 'user', 'obj': new_user} 
    6870                self.log_addition(request, new_user) 
    6971                if "_addanother" in request.POST: 
    70                     request.user.message_set.create(message=msg) 
     72                    compat_add_message(request, messages.SUCCESS, msg) 
    7173                    return HttpResponseRedirect(request.path) 
    7274                elif '_popup' in request.REQUEST: 
    7375                    return self.response_add(request, new_user) 
    7476                else: 
    75                     request.user.message_set.create(message=msg + ' ' + ugettext("You may edit it again below.")) 
     77                    compat_add_message(request, messages.SUCCESS, msg + ' ' + ugettext("You may edit it again below.")) 
    7678                    return HttpResponseRedirect('../%s/' % new_user.id) 
    7779        else: 
    7880            form = self.add_form() 
     
    104106            if form.is_valid(): 
    105107                new_user = form.save() 
    106108                msg = ugettext('Password changed successfully.') 
    107                 request.user.message_set.create(message=msg) 
     109                compat_add_message(request, messages.SUCCESS, msg) 
    108110                return HttpResponseRedirect('..') 
    109111        else: 
    110112            form = self.change_password_form(user) 
  • a/django/contrib/auth/models.py

    old new  
    288288                raise SiteProfileNotAvailable 
    289289        return self._profile_cache 
    290290 
     291    def _get_message_set(self): 
     292        import warnings 
     293        warnings.warn('The user messaging API will be deprecated in Django 1.2.' 
     294                      ' Please update your code to use the new messaging API.', 
     295                      category=PendingDeprecationWarning) 
     296        return self._message_set 
     297    message_set = property(_get_message_set) 
     298 
    291299class Message(models.Model): 
    292300    """ 
    293301    The message system is a lightweight way to queue messages for given 
     
    297305    actions. For example, "The poll Foo was created successfully." is a 
    298306    message. 
    299307    """ 
    300     user = models.ForeignKey(User
     308    user = models.ForeignKey(User, related_name='_message_set'
    301309    message = models.TextField(_('message')) 
    302310 
    303311    def __unicode__(self): 
  • /dev/null

    old new  
     1from constants import * 
  • /dev/null

    old new  
     1""" 
     2Compatibility methods to assist in the transition from user.message_set to the 
     3messages contrib app. 
     4 
     5These methods are for use by Django internally and it is not recommended 
     6that they called directly from a Django project.  They will be removed in a 
     7future version of Django and are not subject to the normal deprecation policy. 
     8""" 
     9 
     10from django.utils.functional import lazy, memoize 
     11 
     12__all__ = ( 
     13    'compat_add_message', 
     14    'compat_get_messages', 
     15) 
     16 
     17 
     18def compat_add_message(request, level, message): 
     19    """ 
     20    Attempts to add a message to the request using the 'messages' app, falling 
     21    back to the user's message_set if MessageMiddleware hasn't been enabled. 
     22    """ 
     23    if hasattr(request, 'messages'): 
     24        request.messages.add(level, message) 
     25    elif hasattr(request, 'user') and request.user.is_authenticated(): 
     26        request.user.message_set.create(message=message) 
     27 
     28 
     29def compat_get_messages(request, get_user=None): 
     30    """ 
     31    Returns the message storage on the request if it exists, otherwise returns 
     32    user.message_set.all() as the old auth context processor did. 
     33    """ 
     34 
     35    def get_user(): 
     36        if hasattr(request, 'user'): 
     37            return request.user 
     38        else: 
     39            from django.contrib.auth.models import AnonymousUser 
     40            return AnonymousUser() 
     41 
     42    if hasattr(request, 'messages'): 
     43        return request.messages 
     44    else: 
     45        return lazy(memoize(get_user().get_and_delete_messages, {}, 0), list)() 
  • /dev/null

    old new  
     1DEBUG = 10 
     2INFO = 20 
     3SUCCESS = 25 
     4WARNING = 30 
     5ERROR = 40 
     6  
     7DEFAULT_TAGS = { 
     8    DEBUG: 'debug', 
     9    INFO: 'info', 
     10    SUCCESS: 'success', 
     11    WARNING: 'warning', 
     12    ERROR: 'error', 
     13} 
  • /dev/null

    old new  
     1from django.contrib.messages.compat import compat_get_messages 
     2 
     3 
     4def messages(request): 
     5    """ 
     6    Returns a lazy 'messages' context variable. 
     7    """ 
     8    return {'messages': compat_get_messages(request)} 
  • /dev/null

    old new  
     1from django.conf import settings 
     2from django.contrib.messages.storage import Storage 
     3 
     4 
     5class MessageMiddleware(object): 
     6    """ 
     7    Middleware that handles temporary messages. 
     8    """ 
     9 
     10    def process_request(self, request): 
     11        request.messages = Storage(request) 
     12 
     13    def process_response(self, request, response): 
     14        """ 
     15        Updates the storage backend (i.e., saves the messages). 
     16         
     17        If not all messages could not be stored and ``DEBUG`` is ``True``, a 
     18        ``ValueError`` is raised. 
     19        """ 
     20        # A higher middleware layer may return a request which does not contain 
     21        # messages storage, so make no assumption that it will be there. 
     22        if hasattr(request, 'messages'): 
     23            unstored_messages = request.messages.update(response) 
     24            if unstored_messages and settings.DEBUG: 
     25                raise ValueError('Not all temporary messages could be stored.') 
     26        return response 
  • /dev/null

    old new  
     1# Models module required so tests are discovered. 
  • /dev/null

    old new  
     1from django.conf import settings 
     2from django.contrib.messages.storage.base import BaseStorage 
     3from django.utils.importcls import get_class 
     4 
     5Storage = get_class(getattr(settings, 'MESSAGE_STORAGE', 
     6    'django.contrib.messages.storage.user_messages.LegacyFallbackStorage')) 
  • /dev/null

    old new  
     1from django.conf import settings 
     2from django.utils.encoding import force_unicode, StrAndUnicode 
     3from django.contrib.messages import constants, utils 
     4 
     5 
     6LEVEL_TAGS = utils.get_level_tags() 
     7 
     8 
     9class 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 __unicode__(self): 
     33        return force_unicode(self.message) 
     34 
     35    def _get_tags(self): 
     36        label_tag = force_unicode(LEVEL_TAGS.get(self.level, ''), 
     37                                  strings_only=True) 
     38        extra_tags = force_unicode(self.extra_tags, strings_only=True) 
     39        if extra_tags: 
     40            if label_tag: 
     41                return u'%s %s' % (extra_tags, label_tag) 
     42            return extra_tags 
     43        return label_tag or '' 
     44 
     45    tags = property(_get_tags) 
     46 
     47 
     48class BaseStorage(object): 
     49    """ 
     50    This is the base backend for temporary message storage. 
     51     
     52    This is not a complete class; to be a usable storage backend, it must be 
     53    subclassed and the two methods ``_get`` and ``_store`` overridden. 
     54    """ 
     55    store_serialized = True 
     56 
     57    def __init__(self, request, *args, **kwargs): 
     58        self.request = request 
     59        self._queued_messages = [] 
     60        self.used = False 
     61        self.added_new = False 
     62        super(BaseStorage, self).__init__(*args, **kwargs) 
     63 
     64    def __len__(self): 
     65        return len(self._loaded_messages) + len(self._queued_messages) 
     66 
     67    def __iter__(self): 
     68        self.used = True 
     69        if self._queued_messages: 
     70            self._loaded_messages.extend(self._queued_messages) 
     71            self._queued_messages = [] 
     72        return iter(self._loaded_messages) 
     73 
     74    def __contains__(self, item): 
     75        return item in self._loaded_messages or item in self._queued_messages 
     76 
     77    @property 
     78    def _loaded_messages(self): 
     79        """ 
     80        Returns a list of loaded messages, retrieving them first if they have 
     81        not been loaded yet. 
     82        """ 
     83        if not hasattr(self, '_loaded_data'): 
     84            messages, all_retrieved = self._get() 
     85            self._loaded_data = messages or [] 
     86        return self._loaded_data 
     87 
     88    def _get(self, *args, **kwargs): 
     89        """ 
     90        Retrieves a list of stored messages. Returns a tuple of the messages and 
     91        a flag indicating whether or not all the messages originally intended 
     92        to be stored in this storage were, in fact, stored and now retrieved. 
     93         
     94        **This method must be implemented by a subclass.** 
     95         
     96        If it is possible to tell if the backend was not used (as opposed to 
     97        just containing no messages) then ``None`` should be returned. 
     98        """ 
     99        raise NotImplementedError() 
     100 
     101    def _store(self, messages, response, *args, **kwargs): 
     102        """ 
     103        Stores a list of messages, returning a list of any messages which could 
     104        not be stored. 
     105         
     106        One type of object must be able to be stored, ``Message``. 
     107         
     108        **This method must be implemented by a subclass.** 
     109        """ 
     110        raise NotImplementedError() 
     111 
     112    def _prepare_messages(self, messages): 
     113        """ 
     114        Prepares a list of messages for storage. 
     115        """ 
     116        if self.store_serialized: 
     117            for message in messages: 
     118                message._prepare() 
     119 
     120    def update(self, response, fail_silently=True): 
     121        """ 
     122        Stores all unread messages. 
     123         
     124        If the backend has yet to be iterated, previously stored messages will 
     125        be stored again. Otherwise, only messages added after the last 
     126        iteration will be stored. 
     127        """ 
     128        if self.used: 
     129            self._prepare_messages(self._queued_messages) 
     130            return self._store(self._queued_messages, response) 
     131        elif self.added_new: 
     132            messages = self._loaded_messages + self._queued_messages 
     133            self._prepare_messages(messages) 
     134            return self._store(messages, response) 
     135 
     136    def add(self, level, message, extra_tags=''): 
     137        """ 
     138        Queues a message to be stored. 
     139         
     140        The message is only queued if it contained something and its level is 
     141        not less than the recording level (``self.level``). 
     142        """ 
     143        if not message: 
     144            return 
     145        # Check that the message level is not less than the recording level. 
     146        level = int(level) 
     147        if level < self.level: 
     148            return 
     149        # Add the message. 
     150        self.added_new = True 
     151        message = Message(level, message, extra_tags=extra_tags) 
     152        self._queued_messages.append(message) 
     153 
     154    def debug(self, message, extra_tags=''): 
     155        """ 
     156        Adds a message with the ``DEBUG`` level. 
     157        """ 
     158        self.add(constants.DEBUG, message, extra_tags=extra_tags) 
     159 
     160    def info(self, message, extra_tags=''): 
     161        """ 
     162        Adds a message with the ``INFO`` level. 
     163        """ 
     164        self.add(constants.INFO, message, extra_tags=extra_tags) 
     165 
     166    def success(self, message, extra_tags=''): 
     167        """ 
     168        Adds a message with the ``SUCCESS`` level. 
     169        """ 
     170        self.add(constants.SUCCESS, message, extra_tags=extra_tags) 
     171 
     172    def warning(self, message, extra_tags=''): 
     173        """ 
     174        Adds a message with the ``WARNING`` level. 
     175        """ 
     176        self.add(constants.WARNING, message, extra_tags=extra_tags) 
     177 
     178    def error(self, message, extra_tags=''): 
     179        """ 
     180        Adds a message with the ``ERROR`` level. 
     181        """ 
     182        self.add(constants.ERROR, message, extra_tags=extra_tags) 
     183 
     184    def _get_level(self): 
     185        """ 
     186        Returns the minimum recorded level. 
     187         
     188        The default level is the ``MESSAGE_LEVEL`` setting. If this is 
     189        not found, the ``INFO`` level is used. 
     190        """ 
     191        if not hasattr(self, '_level'): 
     192            self._level = getattr(settings, 'MESSAGE_LEVEL', constants.INFO) 
     193        return self._level 
     194 
     195    def _set_level(self, value=None): 
     196        """ 
     197        Sets a custom minimum recorded level. 
     198         
     199        If set to ``None``, the default level will be used (see the 
     200        ``_get_level`` method). 
     201        """ 
     202        if value is None and hasattr(self, '_level'): 
     203            del self._level 
     204        else: 
     205            self._level = int(value) 
     206 
     207    level = property(_get_level, _set_level, _set_level) 
  • /dev/null

    old new  
     1import hmac 
     2 
     3from django.conf import settings 
     4from django.utils.hashcompat import sha_constructor 
     5from django.contrib.messages import constants 
     6from django.contrib.messages.storage.base import BaseStorage, Message 
     7try: 
     8    import json   # Available in Python 2.6. 
     9except ImportError: 
     10    # Otherwise fall back to simplejson bundled with Django. 
     11    from django.utils import simplejson as json 
     12 
     13 
     14class MessageEncoder(json.JSONEncoder): 
     15    """ 
     16    Compactly serializes instances of the ``Message`` class as JSON. 
     17    """ 
     18    message_key = '__json_message' 
     19 
     20    def default(self, obj): 
     21        if isinstance(obj, Message): 
     22            message = [self.message_key, obj.level, obj.message] 
     23            if obj.extra_tags: 
     24                message.append(obj.extra_tags) 
     25            return message 
     26        return super(MessageEncoder, self).default(obj) 
     27 
     28 
     29class MessageDecoder(json.JSONDecoder): 
     30    """ 
     31    Decodes JSON that includes serialized ``Message`` instances. 
     32    """ 
     33 
     34    def process_messages(self, obj): 
     35        if isinstance(obj, list) and obj: 
     36            if obj[0] == MessageEncoder.message_key: 
     37                return Message(*obj[1:]) 
     38            return [self.process_messages(item) for item in obj] 
     39        if isinstance(obj, dict): 
     40            return dict([(key, self.process_messages(value)) 
     41                         for key, value in obj.iteritems()]) 
     42        return obj 
     43 
     44    def decode(self, s, **kwargs): 
     45        decoded = super(MessageDecoder, self).decode(s, **kwargs) 
     46        return self.process_messages(decoded) 
     47 
     48 
     49class CookieStorage(BaseStorage): 
     50    """ 
     51    Stores messages in a cookie. 
     52    """ 
     53    cookie_name = 'messages' 
     54    max_cookie_size = 4096 
     55    not_finished = '__messagesnotfinished__' 
     56     
     57    def _get(self, *args, **kwargs): 
     58        """ 
     59        Retrieves a list of messages from the messages cookie.  If the 
     60        not_finished sentinel value is found at the end of the message list, 
     61        remove it and return a result indicating that not all messages were 
     62        retrieved by this storage. 
     63        """ 
     64        data = self.request.COOKIES.get(self.cookie_name) 
     65        messages = self._decode(data) 
     66        all_retrieved = not (messages and messages[-1] == self.not_finished) 
     67        if messages and not all_retrieved: 
     68            # remove the sentinel value 
     69            messages.pop() 
     70        return messages, all_retrieved 
     71 
     72    def _update_cookie(self, encoded_data, response): 
     73        """ 
     74        Either sets the cookie with the encoded data if there is any data to 
     75        store, or deletes the cookie. 
     76        """ 
     77        if encoded_data: 
     78            response.set_cookie(self.cookie_name, encoded_data) 
     79        else: 
     80            response.delete_cookie(self.cookie_name) 
     81 
     82    def _store(self, messages, response, remove_oldest=True, *args, **kwargs): 
     83        """ 
     84        Stores the messages to a cookie, returning a list of any messages which 
     85        could not be stored. 
     86         
     87        If the encoded data is larger than ``max_cookie_size``, removes 
     88        messages until the data fits (these are the messages which are 
     89        returned), and add the not_finished sentinel value to indicate as much. 
     90        """ 
     91        unstored_messages = [] 
     92        encoded_data = self._encode(messages) 
     93        if self.max_cookie_size: 
     94            while encoded_data and len(encoded_data) > self.max_cookie_size: 
     95                if remove_oldest: 
     96                    unstored_messages.append(messages.pop(0)) 
     97                else: 
     98                    unstored_messages.insert(0, messages.pop()) 
     99                encoded_data = self._encode(messages + [self.not_finished], 
     100                                            encode_empty=unstored_messages) 
     101        self._update_cookie(encoded_data, response) 
     102        return unstored_messages 
     103 
     104    def _hash(self, value): 
     105        """ 
     106        Creates an HMAC/SHA1 hash based on the value and the project setting's  
     107        SECRET_KEY, modified to make it unique for the present purpose. 
     108        """ 
     109        key = 'django.contrib.messages' + settings.SECRET_KEY 
     110        return hmac.new(key, value, sha_constructor).hexdigest() 
     111     
     112    def _encode(self, messages, encode_empty=False): 
     113        """ 
     114        Returns an encoded version of the messages list which can be stored as 
     115        plain text. 
     116         
     117        Since the data will be retrieved from the client-side, the encoded data 
     118        also contains a hash to ensure that the data was not tampered with. 
     119        """ 
     120        if messages or encode_empty: 
     121            encoder = MessageEncoder(separators=(',', ':')) 
     122            value = encoder.encode(messages) 
     123            return '%s$%s' % (self._hash(value), value) 
     124 
     125    def _decode(self, data): 
     126        """ 
     127        Safely decodes a encoded text stream back into a list of messages. 
     128         
     129        If the encoded text stream contained an invalid hash or was in an 
     130        invalid format, ``None`` is returned. 
     131        """ 
     132        if not data: 
     133            return None 
     134        bits = data.split('$', 1) 
     135        if len(bits) == 2: 
     136            hash, value = bits 
     137            if hash == self._hash(value): 
     138                try: 
     139                    # If we get here (and the JSON decode works), everything is 
     140                    # good. In any other case, drop back and return None. 
     141                    return json.loads(value, cls=MessageDecoder) 
     142                except ValueError: 
     143                    pass 
     144        # Mark the data as used (so it gets removed) since something was wrong 
     145        # with the data. 
     146        self.used = True 
     147        return None 
  • /dev/null

    old new  
     1from django.contrib.messages.storage.base import BaseStorage 
     2from django.contrib.messages.storage.cookie import CookieStorage 
     3from django.contrib.messages.storage.session import SessionStorage 
     4try: 
     5    set 
     6except NameError: 
     7    from sets import Set as set   # Python 2.3 
     8 
     9 
     10class FallbackStorage(BaseStorage): 
     11    """ 
     12    Tries to store all messages in the first backend, storing any unstored  
     13    messages in each subsequent backend backend. 
     14    """ 
     15    storage_classes = (CookieStorage, SessionStorage) 
     16 
     17    def __init__(self, *args, **kwargs): 
     18        super(FallbackStorage, self).__init__(*args, **kwargs) 
     19        self.storages = [storage_class(*args, **kwargs) 
     20                         for storage_class in self.storage_classes] 
     21        self._used_storages = set() 
     22 
     23    def _get(self, *args, **kwargs): 
     24        """ 
     25        Gets a single list of messages from all storage backends. 
     26        """ 
     27        all_messages = [] 
     28        for storage in self.storages: 
     29            messages, all_retrieved = storage._get() 
     30            # If the backend hasn't been used, no more retrieval is necessary. 
     31            if messages is None: 
     32                break 
     33            if messages: 
     34                self._used_storages.add(storage) 
     35            all_messages.extend(messages) 
     36            # If this storage class contained all the messages, no further 
     37            # retrieval is necessary 
     38            if all_retrieved: 
     39                break 
     40        return all_messages, all_retrieved 
     41 
     42    def _store(self, messages, response, *args, **kwargs): 
     43        """ 
     44        Stores the messages, returning any unstored messages after trying all 
     45        backends. 
     46         
     47        For each storage backend, any messages not stored are passed on to the 
     48        next backend. 
     49        """ 
     50        for storage in self.storages: 
     51            if messages: 
     52                messages = storage._store( 
     53                    messages,  
     54                    response,  
     55                    remove_oldest=False, 
     56                ) 
     57            # Even if there are no more messages, continue iterating to ensure 
     58            # storages which contained messages are flushed. 
     59            elif storage in self._used_storages: 
     60                storage._store([], response) 
     61                self._used_storages.remove(storage) 
     62        return messages 
  • /dev/null

    old new  
     1from django.contrib.messages.storage.base import BaseStorage 
     2 
     3 
     4class SessionStorage(BaseStorage): 
     5    """ 
     6    Stores messages in the session (that is, django.contrib.sessions). 
     7    """ 
     8    session_key = '_messages' 
     9 
     10    def __init__(self, request, *args, **kwargs): 
     11        assert hasattr(request, 'session'), "The session-based temporary "\ 
     12            "message storage requires session middleware to be installed." 
     13        super(SessionStorage, self).__init__(request, *args, **kwargs) 
     14 
     15    def _get(self, *args, **kwargs): 
     16        """ 
     17        Retrieves a list of messages from the request's session.  This storage 
     18        always stores everything it is given, so return True for the 
     19        all_retrieved flag. 
     20        """ 
     21        return self.request.session.get(self.session_key), True 
     22 
     23    def _store(self, messages, response, *args, **kwargs): 
     24        """ 
     25        Stores a list of messages to the request's session. 
     26        """ 
     27        if messages: 
     28            self.request.session[self.session_key] = messages 
     29        else: 
     30            self.request.session.pop(self.session_key, None) 
  • /dev/null

    old new  
     1""" 
     2Storages used to assist in the deprecation of contrib.auth User messages. 
     3 
     4""" 
     5from django.contrib.messages import constants 
     6from django.contrib.messages.storage.base import BaseStorage, Message 
     7from django.contrib.auth.models import User 
     8from django.contrib.messages.storage.fallback import FallbackStorage 
     9 
     10 
     11class UserMessagesStorage(BaseStorage): 
     12    """ 
     13    Retrieves messages from the User, using the legacy user.message_set API. 
     14     
     15    This storage is "read-only" insofar as it can only retrieve and delete  
     16    messages, not store them. 
     17    """ 
     18    session_key = '_messages' 
     19 
     20    def _get_messages_queryset(self): 
     21        """ 
     22        Returns the QuerySet containing all user messages (or ``None`` if 
     23        request.user is not a contrib.auth User). 
     24        """ 
     25        user = getattr(self.request, 'user', None) 
     26        if isinstance(user, User): 
     27            return user.message_set.all() 
     28 
     29    def add(self, *args, **kwargs): 
     30        raise NotImplementedError('This message storage is read-only.') 
     31 
     32    def _get(self, *args, **kwargs): 
     33        """ 
     34        Retrieves a list of messages assigned to the User.  This backend never 
     35        stores anything, so all_retrieved is assumed to be False. 
     36        """ 
     37        queryset = self._get_messages_queryset() 
     38        if queryset is None: 
     39            # This is a read-only and optional storage, so to ensure other 
     40            # storages will also be read if used with FallbackStorage an empty 
     41            # list is returned rather than None. 
     42            return [], False 
     43        messages = [] 
     44        for user_message in queryset: 
     45            messages.append(Message(constants.INFO, user_message.message)) 
     46        return messages, False 
     47 
     48    def _store(self, messages, *args, **kwargs): 
     49        """ 
     50        Removes any messages assigned to the User and returns the list of 
     51        messages (since no messages are stored in this read-only storage). 
     52        """ 
     53        queryset = self._get_messages_queryset() 
     54        if queryset is not None: 
     55            queryset.delete() 
     56        return messages 
     57 
     58 
     59class LegacyFallbackStorage(FallbackStorage): 
     60    """ 
     61    Works like ``FallbackStorage`` but also handles retrieving (and clearing)  
     62    contrib.auth User messages. 
     63    """ 
     64    storage_classes = (UserMessagesStorage,) + FallbackStorage.storage_classes 
  • /dev/null

    old new  
     1from django.contrib.messages.tests.cookie import CookieTest 
     2from django.contrib.messages.tests.fallback import FallbackTest 
     3from django.contrib.messages.tests.middleware import MiddlewareTest 
     4from django.contrib.messages.tests.session import SessionTest 
     5from django.contrib.messages.tests.user_messages import UserMessagesTest, \ 
     6    LegacyFallbackTest 
  • /dev/null

    old new  
     1from django.contrib.messages import constants 
     2import unittest 
     3from django import http 
     4from django.conf import settings 
     5from django.utils.translation import ugettext_lazy 
     6from django.contrib.messages import utils 
     7from django.contrib.messages.storage import Storage, base 
     8from django.contrib.messages.storage.base import Message 
     9 
     10 
     11def add_level_messages(storage): 
     12    """ 
     13    Adds 6 messages from different levels (including a custom one) to a storage 
     14    instance. 
     15    """ 
     16    storage.add(constants.INFO, 'A generic info message') 
     17    storage.add(29, 'Some custom level') 
     18    storage.debug('A debugging message', extra_tags='extra-tag') 
     19    storage.warning('A warning') 
     20    storage.error('An error') 
     21    storage.success('This was a triumph.') 
     22 
     23 
     24class BaseTest(unittest.TestCase): 
     25    storage_class = Storage 
     26    restore_settings = ['MESSAGE_LEVEL', 'MESSAGE_TAGS'] 
     27 
     28    def setUp(self): 
     29        self._remembered_settings = {} 
     30        for setting in self.restore_settings: 
     31            if hasattr(settings, setting): 
     32                self._remembered_settings[setting] = getattr(settings, setting) 
     33                delattr(settings._wrapped, setting) 
     34 
     35    def tearDown(self): 
     36        for setting in self.restore_settings: 
     37            self.restore_setting(setting) 
     38 
     39    def restore_setting(self, setting): 
     40        if setting in self._remembered_settings: 
     41            value = self._remembered_settings.pop(setting) 
     42            setattr(settings, setting, value) 
     43        elif hasattr(settings, setting): 
     44            delattr(settings._wrapped, setting) 
     45 
     46    def get_request(self): 
     47        return http.HttpRequest() 
     48 
     49    def get_response(self): 
     50        return http.HttpResponse() 
     51 
     52    def get_storage(self, data=None): 
     53        """ 
     54        Returns the storage backend, setting its loaded data to the ``data`` 
     55        argument. 
     56         
     57        This method avoids the storage ``_get`` method from getting called so 
     58        that other parts of the storage backend can be tested independent of 
     59        the message retrieval logic. 
     60        """ 
     61        storage = self.storage_class(self.get_request()) 
     62        storage._loaded_data = data or [] 
     63        return storage 
     64 
     65    def test_add(self): 
     66        storage = self.get_storage() 
     67        self.assertFalse(storage.added_new) 
     68        storage.add(constants.INFO, 'Test message 1') 
     69        self.assert_(storage.added_new) 
     70        storage.add(constants.INFO, 'Test message 2', extra_tags='tag') 
     71        self.assertEqual(len(storage), 2) 
     72 
     73    def test_add_lazy_translation(self): 
     74        storage = self.get_storage() 
     75        response = self.get_response() 
     76 
     77        storage.add(constants.INFO, ugettext_lazy('lazy message')) 
     78        storage.update(response) 
     79 
     80        storing = self.stored_messages_count(storage, response) 
     81        self.assertEqual(storing, 1) 
     82 
     83    def test_no_update(self): 
     84        storage = self.get_storage() 
     85        response = self.get_response() 
     86        storage.update(response) 
     87        storing = self.stored_messages_count(storage, response) 
     88        self.assertEqual(storing, 0) 
     89 
     90    def test_add_update(self): 
     91        storage = self.get_storage() 
     92        response = self.get_response() 
     93 
     94        storage.add(constants.INFO, 'Test message 1') 
     95        storage.add(constants.INFO, 'Test message 1', extra_tags='tag') 
     96        storage.update(response) 
     97 
     98        storing = self.stored_messages_count(storage, response) 
     99        self.assertEqual(storing, 2) 
     100 
     101    def test_existing_add_read_update(self): 
     102        storage = self.get_existing_storage() 
     103        response = self.get_response() 
     104 
     105        storage.add(constants.INFO, 'Test message 3') 
     106        list(storage)   # Simulates a read 
     107        storage.update(response) 
     108 
     109        storing = self.stored_messages_count(storage, response) 
     110        self.assertEqual(storing, 0) 
     111 
     112    def test_existing_read_add_update(self): 
     113        storage = self.get_existing_storage() 
     114        response = self.get_response() 
     115 
     116        list(storage)   # Simulates a read 
     117        storage.add(constants.INFO, 'Test message 3') 
     118        storage.update(response) 
     119 
     120        storing = self.stored_messages_count(storage, response) 
     121        self.assertEqual(storing, 1) 
     122 
     123    def stored_messages_count(self, storage, response): 
     124        """ 
     125        Returns the number of messages being stored after a 
     126        ``storage.update()`` call. 
     127        """ 
     128        raise NotImplementedError('This method must be set by a subclass.') 
     129 
     130    def test_get(self): 
     131        raise NotImplementedError('This method must be set by a subclass.') 
     132 
     133    def get_existing_storage(self): 
     134        return self.get_storage([Message(constants.INFO, 'Test message 1'), 
     135                                 Message(constants.INFO, 'Test message 2', 
     136                                              extra_tags='tag')]) 
     137 
     138    def test_existing_read(self): 
     139        """ 
     140        Tests that reading the existing storage doesn't cause the data to be  
     141        lost. 
     142        """ 
     143        storage = self.get_existing_storage() 
     144        self.assertFalse(storage.used) 
     145        # After iterating the storage engine directly, the used flag is set. 
     146        data = list(storage) 
     147        self.assert_(storage.used) 
     148        # The data does not disappear because it has been iterated. 
     149        self.assertEqual(data, list(storage)) 
     150 
     151    def test_existing_add(self): 
     152        storage = self.get_existing_storage() 
     153        self.assertFalse(storage.added_new) 
     154        storage.add(constants.INFO, 'Test message 3') 
     155        self.assert_(storage.added_new) 
     156 
     157    def test_default_level(self): 
     158        storage = self.get_storage() 
     159        add_level_messages(storage) 
     160        self.assertEqual(len(storage), 5) 
     161 
     162    def test_low_level(self): 
     163        storage = self.get_storage() 
     164        storage.level = 5 
     165        add_level_messages(storage) 
     166        self.assertEqual(len(storage), 6) 
     167 
     168    def test_high_level(self): 
     169        storage = self.get_storage() 
     170        storage.level = 30 
     171        add_level_messages(storage) 
     172        self.assertEqual(len(storage), 2) 
     173 
     174    def test_settings_level(self): 
     175        settings.MESSAGE_LEVEL = 29 
     176        storage = self.get_storage() 
     177        add_level_messages(storage) 
     178        self.assertEqual(len(storage), 3) 
     179 
     180    def test_tags(self): 
     181        storage = self.get_storage() 
     182        storage.level = 0 
     183        add_level_messages(storage) 
     184        tags = [msg.tags for msg in storage] 
     185        self.assertEqual(tags, 
     186                         ['info', '', 'extra-tag debug', 'warning', 'error', 
     187                          'success']) 
     188 
     189    def test_custom_tags(self): 
     190        settings.MESSAGE_TAGS = { 
     191            constants.INFO: 'info', 
     192            constants.DEBUG: '', 
     193            constants.WARNING: '', 
     194            constants.ERROR: 'bad', 
     195            29: 'custom', 
     196        } 
     197        # LEVEL_TAGS is a constant defined in the 
     198        # django.contrib.messages.storage.base module, so after changing 
     199        # settings.MESSAGE_TAGS, we need to update that constant too. 
     200        base.LEVEL_TAGS = utils.get_level_tags() 
     201        try: 
     202            storage = self.get_storage() 
     203            storage.level = 0 
     204            add_level_messages(storage) 
     205            tags = [msg.tags for msg in storage] 
     206            self.assertEqual(tags, 
     207                         ['info', 'custom', 'extra-tag', '', 'bad', 'success']) 
     208        finally: 
     209            # Ensure the level tags constant is put back like we found it. 
     210            self.restore_setting('MESSAGE_TAGS') 
     211            base.LEVEL_TAGS = utils.get_level_tags() 
  • /dev/null

    old new  
     1from django.contrib.messages import constants 
     2from django.contrib.messages.tests.base import BaseTest 
     3from django.contrib.messages.storage.cookie import CookieStorage 
     4 
     5 
     6def set_cookie_data(storage, messages, invalid=False, encode_empty=False): 
     7    """ 
     8    Sets ``request.COOKIES`` with the encoded data and removes the storage 
     9    backend's loaded data cache. 
     10    """ 
     11    encoded_data = storage._encode(messages, encode_empty=encode_empty) 
     12    if invalid: 
     13        # Truncate the first character so that the hash is invalid. 
     14        encoded_data = encoded_data[1:] 
     15    storage.request.COOKIES = {CookieStorage.cookie_name: encoded_data} 
     16    if hasattr(storage, '_loaded_data'): 
     17        del storage._loaded_data 
     18 
     19 
     20def stored_cookie_messages_count(storage, response): 
     21    """ 
     22    Returns an integer containing the number of messages stored. 
     23    """ 
     24    # Get a list of cookies, excluding ones with a max-age of 0 (because 
     25    # they have been marked for deletion). 
     26    cookie = response.cookies.get(storage.cookie_name) 
     27    if not cookie or cookie['max-age'] == 0: 
     28        return 0 
     29    data = storage._decode(cookie.value) 
     30    if not data: 
     31        return 0 
     32    if data[-1] == CookieStorage.not_finished: 
     33        data.pop() 
     34    return len(data) 
     35 
     36 
     37class CookieTest(BaseTest): 
     38    storage_class = CookieStorage 
     39 
     40    def stored_messages_count(self, storage, response): 
     41        return stored_cookie_messages_count(storage, response) 
     42 
     43    def test_get(self): 
     44        storage = self.storage_class(self.get_request()) 
     45        # Set initial data. 
     46        example_messages = ['test', 'me'] 
     47        set_cookie_data(storage, example_messages) 
     48        # Test that the message actually contains what we expect. 
     49        self.assertEqual(list(storage), example_messages) 
     50 
     51    def test_get_bad_cookie(self): 
     52        request = self.get_request() 
     53        storage = self.storage_class(request) 
     54        # Set initial (invalid) data. 
     55        example_messages = ['test', 'me'] 
     56        set_cookie_data(storage, example_messages, invalid=True) 
     57        # Test that the message actually contains what we expect. 
     58        self.assertEqual(list(storage), []) 
     59 
     60    def test_max_cookie_length(self): 
     61        """ 
     62        Tests that, if the data exceeds what is allowed in a cookie, older  
     63        messages are removed before saving (and returned by the ``update``  
     64        method). 
     65        """ 
     66        storage = self.get_storage() 
     67        response = self.get_response() 
     68 
     69        for i in range(5): 
     70            storage.add(constants.INFO, str(i) * 900) 
     71        unstored_messages = storage.update(response) 
     72 
     73        cookie_storing = self.stored_messages_count(storage, response) 
     74        self.assertEqual(cookie_storing, 4) 
     75 
     76        self.assertEqual(len(unstored_messages), 1) 
     77        self.assert_(unstored_messages[0].message == '0' * 900) 
  • /dev/null

    old new  
     1from django.contrib.messages import constants 
     2from django.contrib.messages.storage.fallback import FallbackStorage, \ 
     3    CookieStorage 
     4from django.contrib.messages.tests.base import BaseTest 
     5from django.contrib.messages.tests.cookie import set_cookie_data, \ 
     6    stored_cookie_messages_count 
     7from django.contrib.messages.tests.session import set_session_data, \ 
     8    stored_session_messages_count 
     9 
     10 
     11class FallbackTest(BaseTest): 
     12    storage_class = FallbackStorage 
     13 
     14    def get_request(self): 
     15        self.session = {} 
     16        request = super(FallbackTest, self).get_request() 
     17        request.session = self.session 
     18        return request 
     19 
     20    def get_cookie_storage(self, storage): 
     21        return storage.storages[-2] 
     22 
     23    def get_session_storage(self, storage): 
     24        return storage.storages[-1] 
     25 
     26    def stored_cookie_messages_count(self, storage, response): 
     27        return stored_cookie_messages_count(self.get_cookie_storage(storage), 
     28                                            response) 
     29 
     30    def stored_session_messages_count(self, storage, response): 
     31        return stored_session_messages_count(self.get_session_storage(storage)) 
     32 
     33    def stored_messages_count(self, storage, response): 
     34        """ 
     35        Return the storage totals from both cookie and session backends. 
     36        """ 
     37        total = (self.stored_cookie_messages_count(storage, response) + 
     38                 self.stored_session_messages_count(storage, response)) 
     39        return total 
     40 
     41    def test_get(self): 
     42        request = self.get_request() 
     43        storage = self.storage_class(request) 
     44        cookie_storage = self.get_cookie_storage(storage) 
     45 
     46        # Set initial cookie data. 
     47        example_messages = [str(i) for i in range(5)] 
     48        set_cookie_data(cookie_storage, example_messages) 
     49 
     50        # Overwrite the _get method of the fallback storage to prove it is not 
     51        # used (it would cause a TypeError: 'NoneType' object is not callable). 
     52        self.get_session_storage(storage)._get = None 
     53 
     54        # Test that the message actually contains what we expect. 
     55        self.assertEqual(list(storage), example_messages) 
     56 
     57    def test_get_empty(self): 
     58        request = self.get_request() 
     59        storage = self.storage_class(request) 
     60 
     61        # Overwrite the _get method of the fallback storage to prove it is not 
     62        # used (it would cause a TypeError: 'NoneType' object is not callable). 
     63        self.get_session_storage(storage)._get = None 
     64 
     65        # Test that the message actually contains what we expect. 
     66        self.assertEqual(list(storage), []) 
     67 
     68    def test_get_fallback(self): 
     69        request = self.get_request() 
     70        storage = self.storage_class(request) 
     71        cookie_storage = self.get_cookie_storage(storage) 
     72        session_storage = self.get_session_storage(storage) 
     73 
     74        # Set initial cookie and session data. 
     75        example_messages = [str(i) for i in range(5)] 
     76        set_cookie_data(cookie_storage, example_messages[:4] +  
     77                        [CookieStorage.not_finished]) 
     78        set_session_data(session_storage, example_messages[4:]) 
     79 
     80        # Test that the message actually contains what we expect. 
     81        self.assertEqual(list(storage), example_messages) 
     82 
     83    def test_get_fallback_only(self): 
     84        request = self.get_request() 
     85        storage = self.storage_class(request) 
     86        cookie_storage = self.get_cookie_storage(storage) 
     87        session_storage = self.get_session_storage(storage) 
     88 
     89        # Set initial cookie and session data. 
     90        example_messages = [str(i) for i in range(5)] 
     91        set_cookie_data(cookie_storage, [CookieStorage.not_finished],  
     92                        encode_empty=True) 
     93        set_session_data(session_storage, example_messages) 
     94 
     95        # Test that the message actually contains what we expect. 
     96        self.assertEqual(list(storage), example_messages) 
     97 
     98    def test_flush_used_backends(self): 
     99        request = self.get_request() 
     100        storage = self.storage_class(request) 
     101        cookie_storage = self.get_cookie_storage(storage) 
     102        session_storage = self.get_session_storage(storage) 
     103 
     104        # Set initial cookie and session data. 
     105        set_cookie_data(cookie_storage, ['cookie', CookieStorage.not_finished]) 
     106        set_session_data(session_storage, ['session']) 
     107 
     108        # When updating, previously used but no longer needed backends are 
     109        # flushed. 
     110        response = self.get_response() 
     111        list(storage) 
     112        storage.update(response) 
     113        session_storing = self.stored_session_messages_count(storage, response) 
     114        self.assertEqual(session_storing, 0) 
     115 
     116    def test_no_fallback(self): 
     117        """ 
     118        Confirms that: 
     119         
     120        (1) A short number of messages whose data size doesn't exceed what is 
     121        allowed in a cookie will all be stored in the CookieBackend. 
     122         
     123        (2) If the CookieBackend can store all messages, the SessionBackend  
     124        won't be written to at all. 
     125        """ 
     126        storage = self.get_storage() 
     127        response = self.get_response() 
     128 
     129        # Overwrite the _store method of the fallback storage to prove it isn't 
     130        # used (it would cause a TypeError: 'NoneType' object is not callable). 
     131        self.get_session_storage(storage)._store = None 
     132 
     133        for i in range(5): 
     134            storage.add(constants.INFO, str(i) * 100) 
     135        storage.update(response) 
     136 
     137        cookie_storing = self.stored_cookie_messages_count(storage, response) 
     138        self.assertEqual(cookie_storing, 5) 
     139        session_storing = self.stored_session_messages_count(storage, response) 
     140        self.assertEqual(session_storing, 0) 
     141 
     142    def test_session_fallback(self): 
     143        """ 
     144        Confirms that, if the data exceeds what is allowed in a cookie, older  
     145        messages which did not "fit" are stored in the SessionBackend. 
     146        """ 
     147        storage = self.get_storage() 
     148        response = self.get_response() 
     149 
     150        for i in range(5): 
     151            storage.add(constants.INFO, str(i) * 900) 
     152        storage.update(response) 
     153 
     154        cookie_storing = self.stored_cookie_messages_count(storage, response) 
     155        self.assertEqual(cookie_storing, 4) 
     156        session_storing = self.stored_session_messages_count(storage, response) 
     157        self.assertEqual(session_storing, 1) 
     158 
     159    def test_session_fallback_only(self): 
     160        """ 
     161        Confirms that large messages, none of which fit in a cookie, are stored  
     162        in the SessionBackend (and nothing is stored in the CookieBackend). 
     163        """ 
     164        storage = self.get_storage() 
     165        response = self.get_response() 
     166 
     167        storage.add(constants.INFO, 'x' * 5000) 
     168        storage.update(response) 
     169 
     170        cookie_storing = self.stored_cookie_messages_count(storage, response) 
     171        self.assertEqual(cookie_storing, 0) 
     172        session_storing = self.stored_session_messages_count(storage, response) 
     173        self.assertEqual(session_storing, 1) 
  • /dev/null

    old new  
     1import unittest 
     2from django import http 
     3from django.contrib.messages.middleware import MessageMiddleware 
     4 
     5 
     6class MiddlewareTest(unittest.TestCase): 
     7 
     8    def setUp(self): 
     9        self.middleware = MessageMiddleware() 
     10 
     11    def test_response_without_messages(self): 
     12        """ 
     13        Makes sure that the response middleware is tolerant of messages not  
     14        existing on request. 
     15        """ 
     16        request = http.HttpRequest() 
     17        response = http.HttpResponse() 
     18        self.middleware.process_response(request, response) 
  • /dev/null

    old new  
     1from django.contrib.messages.tests.base import BaseTest 
     2from django.contrib.messages.storage.session import SessionStorage 
     3 
     4 
     5def set_session_data(storage, messages): 
     6    """ 
     7    Sets the messages into the backend request's session and remove the 
     8    backend's loaded data cache. 
     9    """ 
     10    storage.request.session[storage.session_key] = messages 
     11    if hasattr(storage, '_loaded_data'): 
     12        del storage._loaded_data 
     13 
     14 
     15def stored_session_messages_count(storage): 
     16    data = storage.request.session.get(storage.session_key, []) 
     17    return len(data) 
     18 
     19 
     20class SessionTest(BaseTest): 
     21    storage_class = SessionStorage 
     22 
     23    def get_request(self): 
     24        self.session = {} 
     25        request = super(SessionTest, self).get_request() 
     26        request.session = self.session 
     27        return request 
     28 
     29    def stored_messages_count(self, storage, response): 
     30        return stored_session_messages_count(storage) 
     31 
     32    def test_get(self): 
     33        storage = self.storage_class(self.get_request()) 
     34        # Set initial data. 
     35        example_messages = ['test', 'me'] 
     36        set_session_data(storage, example_messages) 
     37        # Test that the message actually contains what we expect. 
     38        self.assertEqual(list(storage), example_messages) 
  • /dev/null

    old new  
     1from django import http 
     2from django.contrib.auth.models import User 
     3from django.contrib.messages.storage.user_messages import UserMessagesStorage,\ 
     4    LegacyFallbackStorage 
     5from django.contrib.messages.tests.cookie import set_cookie_data 
     6from django.contrib.messages.tests.fallback import FallbackTest 
     7from django.test import TestCase 
     8 
     9 
     10class UserMessagesTest(TestCase): 
     11 
     12    def setUp(self): 
     13        self.user = User.objects.create(username='tester') 
     14     
     15    def test_add(self): 
     16        storage = UserMessagesStorage(http.HttpRequest()) 
     17        self.assertRaises(NotImplementedError, storage.add, 'Test message 1') 
     18 
     19    def test_get_anonymous(self): 
     20        # Ensure that the storage still works if no user is attached to the 
     21        # request. 
     22        storage = UserMessagesStorage(http.HttpRequest()) 
     23        self.assertEqual(len(storage), 0) 
     24 
     25    def test_get(self): 
     26        storage = UserMessagesStorage(http.HttpRequest()) 
     27        storage.request.user = self.user 
     28        self.user.message_set.create(message='test message') 
     29 
     30        self.assertEqual(len(storage), 1) 
     31        self.assertEqual(list(storage)[0].message, 'test message') 
     32 
     33 
     34class LegacyFallbackTest(FallbackTest, TestCase): 
     35    storage_class = LegacyFallbackStorage 
     36 
     37    def setUp(self): 
     38        super(LegacyFallbackTest, self).setUp() 
     39        self.user = User.objects.create(username='tester') 
     40         
     41    def get_request(self, *args, **kwargs): 
     42        request = super(LegacyFallbackTest, self).get_request(*args, **kwargs) 
     43        request.user = self.user 
     44        return request 
     45 
     46    def test_get_legacy_only(self): 
     47        request = self.get_request() 
     48        storage = self.storage_class(request) 
     49        self.user.message_set.create(message='user message') 
     50 
     51        # Test that the message actually contains what we expect. 
     52        self.assertEqual(len(storage), 1) 
     53        self.assertEqual(list(storage)[0].message, 'user message') 
     54 
     55    def test_get_legacy(self): 
     56        request = self.get_request() 
     57        storage = self.storage_class(request) 
     58        cookie_storage = self.get_cookie_storage(storage) 
     59        self.user.message_set.create(message='user message') 
     60        set_cookie_data(cookie_storage, ['cookie']) 
     61 
     62        # Test that the message actually contains what we expect. 
     63        self.assertEqual(len(storage), 2) 
     64        self.assertEqual(list(storage)[0].message, 'user message') 
     65        self.assertEqual(list(storage)[1], 'cookie') 
  • /dev/null

    old new  
     1from django.conf import settings 
     2from django.contrib.messages import constants  
     3 
     4 
     5def get_level_tags(): 
     6    """ 
     7    Returns the message level tags. 
     8    """ 
     9    level_tags = constants.DEFAULT_TAGS.copy() 
     10    level_tags.update(getattr(settings, 'MESSAGE_TAGS', {})) 
     11    return level_tags 
  • a/django/core/context_processors.py

    old new  
    1010from django.conf import settings 
    1111from django.middleware.csrf import get_token 
    1212from django.utils.functional import lazy, memoize, SimpleLazyObject 
     13from django.contrib.messages.compat import compat_get_messages 
    1314 
    1415def auth(request): 
    1516    """ 
     
    3738 
    3839    return { 
    3940        'user': SimpleLazyObject(get_user), 
    40         'messages': lazy(memoize(lambda: get_user().get_and_delete_messages(), {}, 0), list)(), 
     41        'messages': compat_get_messages(request), 
    4142        'perms':  lazy(lambda: PermWrapper(get_user()), PermWrapper)(), 
    4243    } 
    4344 
  • a/django/core/files/storage.py

    old new  
    88from django.core.files.move import file_move_safe 
    99from django.utils.encoding import force_unicode, smart_str 
    1010from django.utils.functional import LazyObject 
    11 from django.utils.importlib import import_module 
     11from django.utils.importcls import get_class 
    1212from django.utils.text import get_valid_filename 
    1313from django.utils._os import safe_join 
    1414 
     
    221221def get_storage_class(import_path=None): 
    222222    if import_path is None: 
    223223        import_path = settings.DEFAULT_FILE_STORAGE 
    224     try: 
    225         dot = import_path.rindex('.') 
    226     except ValueError: 
    227         raise ImproperlyConfigured("%s isn't a storage module." % import_path) 
    228     module, classname = import_path[:dot], import_path[dot+1:] 
    229     try: 
    230         mod = import_module(module) 
    231     except ImportError, e: 
    232         raise ImproperlyConfigured('Error importing storage module %s: "%s"' % (module, e)) 
    233     try: 
    234         return getattr(mod, classname) 
    235     except AttributeError: 
    236         raise ImproperlyConfigured('Storage module "%s" does not define a "%s" class.' % (module, classname)) 
     224    return get_class(import_path) 
    237225 
    238226class DefaultStorage(LazyObject): 
    239227    def _setup(self): 
  • /dev/null

    old new  
     1from django.utils.importlib import import_module 
     2 
     3 
     4def get_class(import_path): 
     5    """ 
     6    Imports the class described by import_path, where import_path is the full  
     7    Python path to the class. 
     8    """ 
     9    try: 
     10        dot = import_path.rindex('.') 
     11    except ValueError: 
     12        raise ImproperlyConfigured("%s isn't a Python path." % import_path) 
     13    module, classname = import_path[:dot], import_path[dot+1:] 
     14    try: 
     15        mod = import_module(module) 
     16    except ImportError, e: 
     17        raise ImproperlyConfigured('Error importing module %s: "%s"' % (module, e)) 
     18    try: 
     19        return getattr(mod, classname) 
     20    except AttributeError: 
     21        raise ImproperlyConfigured('Module "%s" does not define a "%s" class.' % (module, classname)) 
  • a/django/views/generic/create_update.py

    old new  
    66from django.utils.translation import ugettext 
    77from django.contrib.auth.views import redirect_to_login 
    88from django.views.generic import GenericViewError 
     9from django.contrib import messages 
     10from django.contrib.messages.compat import compat_add_message 
    911 
    1012 
    1113def apply_extra_context(extra_context, context): 
     
    110112        form = form_class(request.POST, request.FILES) 
    111113        if form.is_valid(): 
    112114            new_object = form.save() 
    113             if request.user.is_authenticated(): 
    114                 request.user.message_set.create(message=ugettext("The %(verbose_name)s was created successfully.") % {"verbose_name": model._meta.verbose_name}) 
     115             
     116            compat_add_message(request, messages.SUCCESS, ugettext("The %(verbose_name)s was created successfully.") % {"verbose_name": model._meta.verbose_name}) 
    115117            return redirect(post_save_redirect, new_object) 
    116118    else: 
    117119        form = form_class() 
     
    152154        form = form_class(request.POST, request.FILES, instance=obj) 
    153155        if form.is_valid(): 
    154156            obj = form.save() 
    155             if request.user.is_authenticated(): 
    156                 request.user.message_set.create(message=ugettext("The %(verbose_name)s was updated successfully.") % {"verbose_name": model._meta.verbose_name}) 
     157            compat_add_message(request, messages.SUCCESS, ugettext("The %(verbose_name)s was updated successfully.") % {"verbose_name": model._meta.verbose_name}) 
    157158            return redirect(post_save_redirect, obj) 
    158159    else: 
    159160        form = form_class(instance=obj) 
     
    194195 
    195196    if request.method == 'POST': 
    196197        obj.delete() 
    197         if request.user.is_authenticated(): 
    198             request.user.message_set.create(message=ugettext("The %(verbose_name)s was deleted.") % {"verbose_name": model._meta.verbose_name}) 
     198        compat_add_message(request, messages.SUCCESS, ugettext("The %(verbose_name)s was deleted.") % {"verbose_name": model._meta.verbose_name}) 
    199199        return HttpResponseRedirect(post_delete_redirect) 
    200200    else: 
    201201        if not template_name: 
  • a/docs/index.txt

    old new  
    170170    * :ref:`Internationalization <topics-i18n>` 
    171171    * :ref:`Jython support <howto-jython>` 
    172172    * :ref:`"Local flavor" <ref-contrib-localflavor>` 
     173    * :ref:`Messages <ref-contrib-messages>` 
    173174    * :ref:`Pagination <topics-pagination>` 
    174175    * :ref:`Redirects <ref-contrib-redirects>` 
    175176    * :ref:`Serialization <topics-serialization>` 
  • a/docs/internals/deprecation.txt

    old new  
    2828        * The many to many SQL generation functions on the database backends 
    2929          will be removed.  These have been deprecated since the 1.2 release. 
    3030 
     31        * The ``Message`` model (in ``django.contrib.auth``) and the  
     32          associated methods (such as ``user.message_set.create``), which have 
     33          been deprecated since the 1.2 release, will be removed.  The  
     34          :ref:`messages framework <ref-contrib-messages>` should be used  
     35          instead. 
     36 
    3137    * 2.0 
    3238        * ``django.views.defaults.shortcut()``. This function has been moved 
    3339          to ``django.contrib.contenttypes.views.shortcut()`` as part of the 
  • a/docs/ref/contrib/index.txt

    old new  
    3434   formtools/index 
    3535   humanize 
    3636   localflavor 
     37   messages 
    3738   redirects 
    3839   sitemaps 
    3940   sites 
     
    150151.. _Markdown: http://en.wikipedia.org/wiki/Markdown 
    151152.. _ReST (ReStructured Text): http://en.wikipedia.org/wiki/ReStructuredText 
    152153 
     154messages 
     155======== 
     156 
     157.. versionchanged:: 1.2 
     158    The messages framework was added. 
     159 
     160A framework for storing and retrieving temporary cookie- or session-based 
     161messages 
     162 
     163See the :ref:`messages documentation <ref-contrib-messages>`. 
     164 
    153165redirects 
    154166========= 
    155167 
  • /dev/null

    old new  
     1.. _ref-contrib-messages: 
     2 
     3====================== 
     4The messages framework 
     5====================== 
     6 
     7.. module:: django.contrib.messages 
     8   :synopsis: Provides cookie- and session-based temporary message storage. 
     9 
     10Django provides full support for cookie- and session-based messaging, for 
     11both anonymous and authenticated clients. The message framework allows you 
     12to temporarily store messages in one request and retrieve them for display 
     13in a subsequent request (usually the next one). Every message is tagged 
     14with a specific ``level`` that determines its priority (e.g., ``info``,  
     15``warning``, or ``error``). 
     16 
     17.. versionadded:: 1.2 
     18   The messages framework was added. 
     19 
     20Enabling messages 
     21================= 
     22 
     23Messages are implemented through a :ref:`middleware <ref-middleware>` 
     24class and corresponding :ref:`context processor <ref-templates-api>`. 
     25 
     26To enable message functionality, do the following: 
     27 
     28    * Edit the :setting:`MIDDLEWARE_CLASSES` setting and make sure 
     29      :setting:`MIDDLEWARE_CLASSES` contains ``'django.contrib.messages.middleware.MessageMiddleware'``. 
     30 
     31    * Edit the :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting and make sure 
     32      :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains ``'django.contrib.messages.context_processors.messages'``. 
     33 
     34    * Add ``'django.contrib.messages'`` to your :setting:`INSTALLED_APPS` setting 
     35 
     36The default ``settings.py`` created by ``django-admin.py startproject`` has 
     37``MessageMiddleware`` activated and the ``django.contrib.messages`` app 
     38installed.  Also, the default value for :setting:`TEMPLATE_CONTEXT_PROCESSORS`  
     39contains ``'django.contrib.messages.context_processors.messages'``. 
     40 
     41If you don't want to use messages, you can remove the 
     42``MessageMiddleware`` line from :setting:`MIDDLEWARE_CLASSES`, the ``messages``  
     43context processor from :setting:`TEMPLATE_CONTEXT_PROCESSORS` and  
     44``'django.contrib.messages'`` from your :setting:`INSTALLED_APPS`.  
     45 
     46Configuring the message engine 
     47============================== 
     48 
     49By default, Django first tries to store messages in a lightweight cookie, 
     50and falls back to storing any messages that don't fit in a session variable. 
     51 
     52Storage backends 
     53---------------- 
     54 
     55The messages framework can use different backends to store temporary messages.  
     56To change which backend is being used, add a `MESSAGE_STORAGE`_ to your  
     57settings, referencing the module and class of the storage class. For  
     58example:: 
     59 
     60    MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' 
     61 
     62The value should be the full path of the desired storage class. 
     63 
     64Four storage classes are included: 
     65 
     66``'django.contrib.messages.storage.session.SessionStorage'`` 
     67    This class stores all messages inside of the request's session. It 
     68    requires Django's ``contrib.session`` application. 
     69 
     70``'django.contrib.messages.storage.cookie.CookieStorage'`` 
     71    This class stores the message data in a cookie (signed with a secret hash to  
     72    prevent manipulation) to persist notifications across requests. Old messages  
     73    are dropped if the cookie data size would exceed 4096 bytes. 
     74 
     75``'django.contrib.messages.storage.fallback.FallbackStorage'`` 
     76    This class first uses CookieStorage for all messages, falling back to using  
     77    SessionStorage for the messages that could not fit in a single cookie. 
     78 
     79    Since it is uses SessionStorage, it also requires Django's 
     80    ``contrib.session`` application.  
     81 
     82``'django.contrib.messages.storage.user_messages.LegacyFallbackStorage'`` 
     83    This is the default temporary storage class. 
     84 
     85    This class extends FallbackStorage and adds compatibility methods to 
     86    to retrieve any messages stored in the user Message model by code that 
     87    has not yet been updated to use the new API. This storage is temporary 
     88    (because it makes use of code that is pending deprecation) and will be  
     89    removed in Django 1.4. At that time, the default storage will become 
     90    ``django.contrib.messages.storage.fallback.FallbackStorage``. For more 
     91    information, see `LegacyFallbackStorage`_ below. 
     92 
     93To write your own storage class, subclass the ``BaseStorage`` class in 
     94``django.contrib.messages.storage.base`` and implement the ``_get`` and  
     95``_store`` methods. 
     96 
     97LegacyFallbackStorage 
     98^^^^^^^^^^^^^^^^^^^^^ 
     99 
     100The ``LegacyFallbackStorage`` is a temporary tool to facilitate the transition 
     101from the deprecated ``user.message_set`` API and will be removed in Django 1.4. 
     102 
     103In addition to the functionality in the ``FallbackStorage``, it adds a custom, 
     104read-only storage class that retrieves messages from the user ``Message`` 
     105model.  Any messages that were stored in the ``Message`` model (e.g., by code 
     106that has not yet been updated to use the messages framework) will be 
     107retrieved first, followed those stored in a cookie and session, if any. 
     108Since messages stored in the ``Message`` model do not have a concept of levels, 
     109they will be assigned the ``INFO`` level by default. 
     110 
     111Existing coding that calls ``user.message_set.create`` will continue to work 
     112in Django 1.2 and Django 1.3.  In Django 1.2, the method raises a 
     113``PendingDeprecationWarning`` (which is not shown by default).  In Django 1.3, 
     114the method will raise a louder ``DeprecationWarning`` stating that the code at  
     115fault needs to be updated.  Any code that uses ``user.message_set`` must be  
     116updated to use the messages framework before Django 1.4, when the old API will  
     117be removed. 
     118 
     119 
     120Message levels 
     121-------------- 
     122 
     123The messages framework is based on a configurable level architecture similar 
     124to that of the Python logging module.  Message levels allow you to group  
     125messages by type so they can be filtered or displayed differently in views and  
     126templates. 
     127 
     128The built-in levels (which can be imported from ``django.contrib.messages``  
     129directly) are: 
     130 
     131=========== ======== 
     132Constant    Purpose 
     133=========== ======== 
     134``DEBUG``   Development-related messages that will be ignored (or removed) in a production deployment  
     135``INFO``    Informational messages for the user 
     136``SUCCESS`` An action was successful, e.g. "Your profile was updated successfully" 
     137``WARNING`` A failure did not occur but may be imminent 
     138``ERROR``   An action was **not** successful or some other failure occurred 
     139=========== ======== 
     140 
     141The `MESSAGE_LEVEL`_ setting can be used to change the minimum recorded 
     142level. Attempts to add messages of a level less than this will be ignored. 
     143 
     144Message tags 
     145------------ 
     146 
     147Message tags are a string representation of the message level plus any 
     148extra tags that were added directly in the view (see  
     149`Adding extra message tags`_ below for more details).  Tags are stored in a 
     150string and are separated by spaces.  Typically, message tags 
     151are used as CSS classes to customize message style based on message type. By  
     152default, each level has a single tag that's a lowercase version of its own 
     153constant: 
     154 
     155==============  =========== 
     156Level Constant  Tag 
     157==============  =========== 
     158``DEBUG``       ``debug`` 
     159``INFO``        ``info`` 
     160``SUCCESS``     ``success`` 
     161``WARNING``     ``warning`` 
     162``ERROR``       ``error`` 
     163==============  =========== 
     164 
     165To change the default tags for a message level (either built-in or custom),  
     166set the `MESSAGE_TAGS`_ setting to a dictionary containing the levels  
     167you wish to change. As this extends the default tags, you only need to provide  
     168tags for the levels you wish to override:: 
     169 
     170    from django.contrib.messages import constants as messages 
     171    MESSAGE_TAGS = { 
     172        messages.INFO: '', 
     173        50: 'critical', 
     174    } 
     175 
     176Using messages in views and templates 
     177===================================== 
     178 
     179Adding a message 
     180---------------- 
     181 
     182The middleware attaches an instance of a temporary storage class called 
     183``messages`` to your ``request``. To add a message, call:: 
     184 
     185    request.messages.add(messages.INFO, 'Hello world.') 
     186 
     187Some other methods provide a standard way to add messages with commonly 
     188used tags (which are usually represented as HTML classes for the message):: 
     189 
     190    request.messages.debug('%s SQL statements were executed.' % count) 
     191    request.messages.info('Three credits remain in your account.') 
     192    request.messages.success('Profile details updated.') 
     193    request.messages.warning('Your account expires in three days.') 
     194    request.messages.error('Document deleted.') 
     195 
     196Displaying messages 
     197------------------- 
     198 
     199In your template, use something like:: 
     200 
     201        {% if messages %} 
     202        <ul class="messages"> 
     203                {% for message in messages %} 
     204                <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li> 
     205                {% endfor %} 
     206        </ul> 
     207        {% endif %} 
     208 
     209If you're using the context processor, your template should be rendered with a 
     210``RequestContext``. Otherwise, ensure ``request.messages`` is available to 
     211the template context. 
     212 
     213Creating custom message levels 
     214------------------------------ 
     215 
     216Messages levels are nothing more than integers, so you can define your own 
     217level constants and use them to create more customized user feedback, e.g.:: 
     218 
     219    CRITICAL = 50 
     220     
     221    def my_view(request): 
     222        request.messages.add(CRITICAL, 'A serious error occured.') 
     223 
     224When creating custom message levels you should be careful to avoid overloading 
     225existing levels.  The values for the built-in levels are: 
     226 
     227==============  ===== 
     228Level Constant  Value 
     229==============  ===== 
     230``DEBUG``       10 
     231``INFO``        20 
     232``SUCCESS``     25 
     233``WARNING``     30 
     234``ERROR``       40 
     235==============  ===== 
     236 
     237If you need to identify the custom levels in your HTML or CSS, you need to  
     238provide a mapping via the `MESSAGE_TAGS`_ setting. 
     239 
     240**Note:** If you are creating a reusable application, it is recommended to use 
     241only the built-in `message levels`_ and not rely on any custom levels. 
     242 
     243Changing the minimum recorded level per-request 
     244----------------------------------------------- 
     245 
     246The minimum recorded level can be set per request by changing the ``level``  
     247attribute of the messages storage instance:: 
     248 
     249    from django.contrib import messages 
     250     
     251    # Change the messages level to ensure the debug message is added. 
     252    request.messages.level = messages.DEBUG 
     253    request.messages.debug('Test message...') 
     254     
     255    # In another request, record only messages with a level of WARNING and higher 
     256    request.messages.level = messages.WARNING 
     257    request.messages.success('Your profile was updated.') # ignored 
     258    request.messages.warning('Your account is about to expire.') # recorded 
     259     
     260    # Set the messages level back to default. 
     261    request.messages.level = None 
     262 
     263For more information, on how the minimum recorded level functions, see  
     264`Message levels`_ above. 
     265 
     266Adding extra message tags 
     267------------------------- 
     268 
     269For more direct control over message tags, you can optionally provide a string 
     270containing extra tags to any of the add methods:: 
     271 
     272    request.messages.add(messages.INFO, 'Over 9000!', extra_tags='dragonball') 
     273    request.messages.error('Email box full', extra_tags='email') 
     274 
     275Extra tags are added before the default tag for that level and are space  
     276separated. 
     277 
     278Expiration of messages 
     279====================== 
     280 
     281The messages are marked to be cleared when the storage instance is iterated  
     282(and cleared when the response is processed). 
     283 
     284To avoid the messages being cleared, you can set 
     285``request.messages.used = False`` after iterating. 
     286 
     287Settings 
     288======== 
     289 
     290A few :ref:`Django settings <ref-settings>` give you control over message behavior: 
     291 
     292MESSAGE_LEVEL 
     293------------- 
     294 
     295Default: ``messages.INFO`` 
     296 
     297Important: If you override this in your settings file and rely on any of the built-in  
     298constants, you must import the constants module directly to avoid the potential 
     299for circular imports. 
     300 
     301This sets the minimum message that will be saved in the message storage.  See 
     302`Message levels`_ above for more details. 
     303 
     304MESSAGE_STORAGE 
     305--------------- 
     306 
     307Default: ``'django.contrib.messages.storage.user_messages.LegacyFallbackStorage'`` 
     308 
     309Controls where Django stores message data. Valid values are: 
     310 
     311    * ``'django.contrib.messages.storage.fallback.FallbackStorage'`` 
     312    * ``'django.contrib.messages.storage.session.SessionStorage'`` 
     313    * ``'django.contrib.messages.storage.cookie.CookieStorage'`` 
     314    * ``'django.contrib.messages.storage.user_messages.LegacyFallbackStorage'`` 
     315 
     316See `Storage backends`_ for more details. 
     317 
     318MESSAGE_TAGS 
     319------------ 
     320 
     321Default:: 
     322     
     323        {messages.DEBUG: 'debug', 
     324        messages.INFO: 'info', 
     325        messages.SUCCESS: 'success', 
     326        messages.WARNING: 'warning', 
     327        messages.ERROR: 'error',} 
     328 
     329Important: If you override this in your settings file and rely on any of the built-in  
     330constants, you must import the constants module directly to avoid the potential 
     331for circular imports. 
     332 
     333This sets the mapping of message level to message tag, which is typically 
     334rendered as a CSS class in HTML. If you specify a value, it will extend 
     335the default. This means you only have to specify those values which you need 
     336to override. See `Displaying messages`_ above for more details. 
     337 
     338.. _Django settings: ../settings/ 
     339 
  • a/docs/ref/settings.txt

    old new  
    812812 
    813813.. setting:: MIDDLEWARE_CLASSES 
    814814 
     815MESSAGE_LEVEL 
     816------------- 
     817 
     818.. versionadded:: 1.2 
     819 
     820Default: `messages.INFO` 
     821 
     822Sets the minimum message level that will be recorded by the messages  
     823framework. See the :ref:`messages documentation <ref-contrib-messages>` for 
     824more details. 
     825 
     826MESSAGE_STORAGE 
     827--------------- 
     828 
     829.. versionadded:: 1.2 
     830 
     831Default: ``'django.contrib.messages.storage.user_messages.LegacyFallbackStorage'`` 
     832 
     833Controls where Django stores message data.  See the 
     834:ref:`messages documentation <ref-contrib-messages>` for more details. 
     835 
     836MESSAGE_TAGS 
     837------------ 
     838 
     839.. versionadded:: 1.2 
     840 
     841Default:: 
     842 
     843        {messages.DEBUG: 'debug', 
     844        messages.INFO: 'info', 
     845        messages.SUCCESS: 'success', 
     846        messages.WARNING: 'warning', 
     847        messages.ERROR: 'error',} 
     848 
     849Sets the mapping of message levels to message tags. See the 
     850:ref:`messages documentation <ref-contrib-messages>` for more details. 
     851 
    815852MIDDLEWARE_CLASSES 
    816853------------------ 
    817854 
     
    820857    ('django.middleware.common.CommonMiddleware', 
    821858     'django.contrib.sessions.middleware.SessionMiddleware', 
    822859     'django.middleware.csrf.CsrfViewMiddleware', 
    823      'django.contrib.auth.middleware.AuthenticationMiddleware',) 
     860     'django.contrib.auth.middleware.AuthenticationMiddleware', 
     861     'django.contrib.messages.middleware.MessageMiddleware',) 
    824862 
    825863A tuple of middleware classes to use. See :ref:`topics-http-middleware`. 
    826864 
     
    10591097    ("django.core.context_processors.auth", 
    10601098    "django.core.context_processors.debug", 
    10611099    "django.core.context_processors.i18n", 
    1062     "django.core.context_processors.media") 
     1100    "django.core.context_processors.media", 
     1101    "django.contrib.messages.context_processors.messages") 
    10631102 
    10641103A tuple of callables that are used to populate the context in ``RequestContext``. 
    10651104These callables take a request object as their argument and return a dictionary 
  • a/docs/ref/templates/api.txt

    old new  
    311311    ("django.core.context_processors.auth", 
    312312    "django.core.context_processors.debug", 
    313313    "django.core.context_processors.i18n", 
    314     "django.core.context_processors.media") 
     314    "django.core.context_processors.media", 
     315    "django.contrib.messages.context_processors.messages") 
    315316 
    316317.. versionadded:: 1.2 
    317318   In addition to these, ``RequestContext`` always uses 
     
    320321   in case of accidental misconfiguration, it is deliberately hardcoded in and 
    321322   cannot be turned off by the :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting. 
    322323 
     324.. versionadded:: 1.2 
     325   The ``'messages'`` context processor was added.  For more information, see  
     326   the :ref:`messages documentation <ref-contrib-messages>`. 
     327 
    323328Each processor is applied in order. That means, if one processor adds a 
    324329variable to the context and a second processor adds a variable with the same 
    325330name, the second will override the first. The default processors are explained 
     
    365370      logged-in user (or an ``AnonymousUser`` instance, if the client isn't 
    366371      logged in). 
    367372 
    368     * ``messages`` -- A list of messages (as strings) for the currently 
    369       logged-in user. Behind the scenes, this calls 
    370       ``request.user.get_and_delete_messages()`` for every request. That method 
    371       collects the user's messages and deletes them from the database. 
    372  
    373       Note that messages are set with ``user.message_set.create``. 
     373    * ``messages`` -- A list of messages (as strings) that have been set 
     374      via the user model (using ``user.message_set.create``) or through 
     375      the :ref:`messages framework <ref-contrib-messages>`. 
    374376 
    375377    * ``perms`` -- An instance of 
    376378      ``django.core.context_processors.PermWrapper``, representing the 
    377379      permissions that the currently logged-in user has. 
    378380 
     381.. versionchanged:: 1.2 
     382   The ``messages`` variable was changed to include any messages added 
     383   via the :ref:`messages framework <ref-contrib-messages`. 
     384 
    379385django.core.context_processors.debug 
    380386~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    381387 
     
    427433:class:`~django.http.HttpRequest`. Note that this processor is not enabled by default; 
    428434you'll have to activate it. 
    429435 
     436django.contrib.messages.context_processors.messages 
     437~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     438 
     439If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every 
     440``RequestContext`` will contain a single additional variable: 
     441 
     442    * ``messages`` -- A list of messages (as strings) that have been set 
     443      via the user model (using ``user.message_set.create``) or through 
     444      the :ref:`messages framework <ref-contrib-messages>`. 
     445 
     446.. versionadded:: 1.2 
     447   This template context variable was previously supplied by the ``'auth'`` 
     448   context processor.  For backwards compatibility the ``'auth'`` context 
     449   processor will continue to supply the ``messages`` variable until Django 
     450   1.4.  If you use the ``messages`` variable, your project will work with 
     451   either (or both) context processors, but it is recommended to add  
     452   ``django.contrib.messages.context_processors.messages`` so your project 
     453   will be prepared for the future upgrade. 
     454 
    430455Writing your own context processors 
    431456~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    432457 
  • a/docs/topics/auth.txt

    old new  
    12891289Messages 
    12901290======== 
    12911291 
     1292**NOTE:** This functionality is pending deprecation and will be removed in  
     1293Django 1.4.  It is recommended to use the  
     1294:ref:`messages framework <ref-contrib-messages>` for new projects whenever  
     1295possible. 
     1296 
    12921297The message system is a lightweight way to queue messages for given users. 
    12931298 
    12941299A message is associated with a :class:`~django.contrib.auth.models.User`. 
     
    13341339    </ul> 
    13351340    {% endif %} 
    13361341 
    1337 Note that :class:`~django.template.context.RequestContext` calls 
    1338 :meth:`~django.contrib.auth.models.User.get_and_delete_messages` behind the 
    1339 scenes, so any messages will be deleted even if you don't display them. 
     1342.. versionchanged:: 1.2 
     1343   The ``messages`` template variable uses a backwards compatible method in the 
     1344   :ref:`messages framework <ref-contrib-messages>` to retrieve messages from 
     1345   both the user ``Message`` model and from the new framework.  Unlike in 
     1346   previous revisions, the messages will not be erased unless they are actually 
     1347   displayed. 
    13401348 
    13411349Finally, note that this messages framework only works with users in the user 
    13421350database. To send messages to anonymous users, use the 
    1343 :ref:`session framework <topics-http-sessions>`. 
     1351:ref:`messages framework <ref-contrib-messages>`. 
    13441352 
    13451353.. _authentication-backends: 
    13461354 
  • a/tests/runtests.py

    old new  
    2828    'django.contrib.flatpages', 
    2929    'django.contrib.redirects', 
    3030    'django.contrib.sessions', 
     31    'django.contrib.messages', 
    3132    'django.contrib.comments', 
    3233    'django.contrib.admin', 
    3334] 
     
    107108    settings.MIDDLEWARE_CLASSES = ( 
    108109        'django.contrib.sessions.middleware.SessionMiddleware', 
    109110        'django.contrib.auth.middleware.AuthenticationMiddleware', 
     111        'django.contrib.messages.middleware.MessageMiddleware', 
    110112        'django.middleware.common.CommonMiddleware', 
    111113    ) 
    112114    settings.SITE_ID = 1