Advanced deprecation of user-based messages and the LegacyFallbackStorage in contrib.messages.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@15975 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
c349aad412
commit
5d5149cd18
|
@ -502,7 +502,7 @@ CSRF_COOKIE_DOMAIN = None
|
|||
############
|
||||
|
||||
# Class to use as messges backend
|
||||
MESSAGE_STORAGE = 'django.contrib.messages.storage.user_messages.LegacyFallbackStorage'
|
||||
MESSAGE_STORAGE = 'django.contrib.messages.storage.fallback.FallbackStorage'
|
||||
|
||||
# Default values of MESSAGE_LEVEL and MESSAGE_TAGS are defined within
|
||||
# django.contrib.messages to avoid imports in this settings file.
|
||||
|
|
|
@ -345,13 +345,6 @@ class User(models.Model):
|
|||
|
||||
return _user_has_module_perms(self, app_label)
|
||||
|
||||
def get_and_delete_messages(self):
|
||||
messages = []
|
||||
for m in self.message_set.all():
|
||||
messages.append(m.message)
|
||||
m.delete()
|
||||
return messages
|
||||
|
||||
def email_user(self, subject, message, from_email=None):
|
||||
"Sends an email to this User."
|
||||
from django.core.mail import send_mail
|
||||
|
@ -387,21 +380,6 @@ class User(models.Model):
|
|||
return self._profile_cache
|
||||
|
||||
|
||||
class Message(models.Model):
|
||||
"""
|
||||
The message system is a lightweight way to queue messages for given
|
||||
users. A message is associated with a User instance (so it is only
|
||||
applicable for registered users). There's no concept of expiration or
|
||||
timestamps. Messages are created by the Django admin after successful
|
||||
actions. For example, "The poll Foo was created successfully." is a
|
||||
message.
|
||||
"""
|
||||
user = models.ForeignKey(User, related_name='_message_set')
|
||||
message = models.TextField(_('message'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.message
|
||||
|
||||
class AnonymousUser(object):
|
||||
id = None
|
||||
username = ''
|
||||
|
|
|
@ -20,8 +20,6 @@ def add_message(request, level, message, extra_tags='', fail_silently=False):
|
|||
"""
|
||||
if hasattr(request, '_messages'):
|
||||
return request._messages.add(level, message, extra_tags)
|
||||
if hasattr(request, 'user') and request.user.is_authenticated():
|
||||
return request.user.message_set.create(message=message)
|
||||
if not fail_silently:
|
||||
raise MessageFailure('Without the django.contrib.messages '
|
||||
'middleware, messages can only be added to '
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
"""
|
||||
Storages used to assist in the deprecation of contrib.auth User messages.
|
||||
|
||||
"""
|
||||
from django.contrib.messages import constants
|
||||
from django.contrib.messages.storage.base import BaseStorage, Message
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.messages.storage.fallback import FallbackStorage
|
||||
|
||||
|
||||
class UserMessagesStorage(BaseStorage):
|
||||
"""
|
||||
Retrieves messages from the User, using the legacy user.message_set API.
|
||||
|
||||
This storage is "read-only" insofar as it can only retrieve and delete
|
||||
messages, not store them.
|
||||
"""
|
||||
session_key = '_messages'
|
||||
|
||||
def _get_messages_queryset(self):
|
||||
"""
|
||||
Returns the QuerySet containing all user messages (or ``None`` if
|
||||
request.user is not a contrib.auth User).
|
||||
"""
|
||||
user = getattr(self.request, 'user', None)
|
||||
if isinstance(user, User):
|
||||
return user._message_set.all()
|
||||
|
||||
def add(self, *args, **kwargs):
|
||||
raise NotImplementedError('This message storage is read-only.')
|
||||
|
||||
def _get(self, *args, **kwargs):
|
||||
"""
|
||||
Retrieves a list of messages assigned to the User. This backend never
|
||||
stores anything, so all_retrieved is assumed to be False.
|
||||
"""
|
||||
queryset = self._get_messages_queryset()
|
||||
if queryset is None:
|
||||
# This is a read-only and optional storage, so to ensure other
|
||||
# storages will also be read if used with FallbackStorage an empty
|
||||
# list is returned rather than None.
|
||||
return [], False
|
||||
messages = []
|
||||
for user_message in queryset:
|
||||
messages.append(Message(constants.INFO, user_message.message))
|
||||
return messages, False
|
||||
|
||||
def _store(self, messages, *args, **kwargs):
|
||||
"""
|
||||
Removes any messages assigned to the User and returns the list of
|
||||
messages (since no messages are stored in this read-only storage).
|
||||
"""
|
||||
queryset = self._get_messages_queryset()
|
||||
if queryset is not None:
|
||||
queryset.delete()
|
||||
return messages
|
||||
|
||||
|
||||
class LegacyFallbackStorage(FallbackStorage):
|
||||
"""
|
||||
Works like ``FallbackStorage`` but also handles retrieving (and clearing)
|
||||
contrib.auth User messages.
|
||||
"""
|
||||
storage_classes = (UserMessagesStorage,) + FallbackStorage.storage_classes
|
|
@ -2,5 +2,3 @@ from django.contrib.messages.tests.cookie import CookieTest
|
|||
from django.contrib.messages.tests.fallback import FallbackTest
|
||||
from django.contrib.messages.tests.middleware import MiddlewareTest
|
||||
from django.contrib.messages.tests.session import SessionTest
|
||||
from django.contrib.messages.tests.user_messages import \
|
||||
UserMessagesTest, LegacyFallbackTest
|
||||
|
|
|
@ -60,9 +60,6 @@ class BaseTest(TestCase):
|
|||
self.storage_class.__name__)
|
||||
self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
|
||||
settings.TEMPLATE_DIRS = ()
|
||||
self.save_warnings_state()
|
||||
warnings.filterwarnings('ignore', category=DeprecationWarning,
|
||||
module='django.contrib.auth.models')
|
||||
|
||||
def tearDown(self):
|
||||
for setting in self.restore_settings:
|
||||
|
@ -74,7 +71,6 @@ class BaseTest(TestCase):
|
|||
settings.INSTALLED_APPS = self._installed_apps
|
||||
settings.MESSAGE_STORAGE = self._message_storage
|
||||
settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
|
||||
self.restore_warnings_state()
|
||||
|
||||
def restore_setting(self, setting):
|
||||
if setting in self._remembered_settings:
|
||||
|
@ -226,44 +222,6 @@ class BaseTest(TestCase):
|
|||
for msg in data['messages']:
|
||||
self.assertContains(response, msg)
|
||||
|
||||
@skipUnlessAuthIsInstalled
|
||||
def test_middleware_disabled_auth_user(self):
|
||||
"""
|
||||
Tests that the messages API successfully falls back to using
|
||||
user.message_set to store messages directly when the middleware is
|
||||
disabled.
|
||||
"""
|
||||
settings.MESSAGE_LEVEL = constants.DEBUG
|
||||
user = User.objects.create_user('test', 'test@example.com', 'test')
|
||||
self.client.login(username='test', password='test')
|
||||
settings.INSTALLED_APPS = list(settings.INSTALLED_APPS)
|
||||
settings.INSTALLED_APPS.remove(
|
||||
'django.contrib.messages',
|
||||
)
|
||||
settings.MIDDLEWARE_CLASSES = list(settings.MIDDLEWARE_CLASSES)
|
||||
settings.MIDDLEWARE_CLASSES.remove(
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
)
|
||||
settings.TEMPLATE_CONTEXT_PROCESSORS = \
|
||||
list(settings.TEMPLATE_CONTEXT_PROCESSORS)
|
||||
settings.TEMPLATE_CONTEXT_PROCESSORS.remove(
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
)
|
||||
data = {
|
||||
'messages': ['Test message %d' % x for x in xrange(10)],
|
||||
}
|
||||
show_url = reverse('django.contrib.messages.tests.urls.show')
|
||||
for level in ('debug', 'info', 'success', 'warning', 'error'):
|
||||
add_url = reverse('django.contrib.messages.tests.urls.add',
|
||||
args=(level,))
|
||||
response = self.client.post(add_url, data, follow=True)
|
||||
self.assertRedirects(response, show_url)
|
||||
self.assertTrue('messages' in response.context)
|
||||
context_messages = list(response.context['messages'])
|
||||
for msg in data['messages']:
|
||||
self.assertTrue(msg in context_messages)
|
||||
self.assertContains(response, msg)
|
||||
|
||||
def test_middleware_disabled_anon_user(self):
|
||||
"""
|
||||
Tests that, when the middleware is disabled and a user is not logged
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
from django import http
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.messages.storage.user_messages import UserMessagesStorage,\
|
||||
LegacyFallbackStorage
|
||||
from django.contrib.messages.tests.base import skipUnlessAuthIsInstalled
|
||||
from django.contrib.messages.tests.cookie import set_cookie_data
|
||||
from django.contrib.messages.tests.fallback import FallbackTest
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class UserMessagesTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(username='tester')
|
||||
|
||||
def test_add(self):
|
||||
storage = UserMessagesStorage(http.HttpRequest())
|
||||
self.assertRaises(NotImplementedError, storage.add, 'Test message 1')
|
||||
|
||||
def test_get_anonymous(self):
|
||||
# Ensure that the storage still works if no user is attached to the
|
||||
# request.
|
||||
storage = UserMessagesStorage(http.HttpRequest())
|
||||
self.assertEqual(len(storage), 0)
|
||||
|
||||
def test_get(self):
|
||||
storage = UserMessagesStorage(http.HttpRequest())
|
||||
storage.request.user = self.user
|
||||
self.user.message_set.create(message='test message')
|
||||
|
||||
self.assertEqual(len(storage), 1)
|
||||
self.assertEqual(list(storage)[0].message, 'test message')
|
||||
|
||||
UserMessagesTest = skipUnlessAuthIsInstalled(UserMessagesTest)
|
||||
|
||||
|
||||
class LegacyFallbackTest(FallbackTest, TestCase):
|
||||
storage_class = LegacyFallbackStorage
|
||||
|
||||
def setUp(self):
|
||||
super(LegacyFallbackTest, self).setUp()
|
||||
self.user = User.objects.create(username='tester')
|
||||
|
||||
def get_request(self, *args, **kwargs):
|
||||
request = super(LegacyFallbackTest, self).get_request(*args, **kwargs)
|
||||
request.user = self.user
|
||||
return request
|
||||
|
||||
def test_get_legacy_only(self):
|
||||
request = self.get_request()
|
||||
storage = self.storage_class(request)
|
||||
self.user.message_set.create(message='user message')
|
||||
|
||||
# Test that the message actually contains what we expect.
|
||||
self.assertEqual(len(storage), 1)
|
||||
self.assertEqual(list(storage)[0].message, 'user message')
|
||||
|
||||
def test_get_legacy(self):
|
||||
request = self.get_request()
|
||||
storage = self.storage_class(request)
|
||||
cookie_storage = self.get_cookie_storage(storage)
|
||||
self.user.message_set.create(message='user message')
|
||||
set_cookie_data(cookie_storage, ['cookie'])
|
||||
|
||||
# Test that the message actually contains what we expect.
|
||||
self.assertEqual(len(storage), 2)
|
||||
self.assertEqual(list(storage)[0].message, 'user message')
|
||||
self.assertEqual(list(storage)[1], 'cookie')
|
||||
|
||||
LegacyFallbackTest = skipUnlessAuthIsInstalled(LegacyFallbackTest)
|
|
@ -83,37 +83,10 @@ Four storage classes are included:
|
|||
Since it is uses SessionStorage, it also requires Django's
|
||||
``contrib.sessions`` application.
|
||||
|
||||
``'django.contrib.messages.storage.user_messages.LegacyFallbackStorage'``
|
||||
This is the default temporary storage class.
|
||||
|
||||
This class extends FallbackStorage and adds compatibility methods
|
||||
to retrieve any messages stored in the user Message model by code that
|
||||
has not yet been updated to use the new API. This storage is temporary
|
||||
(because it makes use of code that is pending deprecation) and will be
|
||||
removed in Django 1.4. At that time, the default storage will become
|
||||
``django.contrib.messages.storage.fallback.FallbackStorage``. For more
|
||||
information, see `LegacyFallbackStorage`_ below.
|
||||
|
||||
To write your own storage class, subclass the ``BaseStorage`` class in
|
||||
``django.contrib.messages.storage.base`` and implement the ``_get`` and
|
||||
``_store`` methods.
|
||||
|
||||
LegacyFallbackStorage
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``LegacyFallbackStorage`` is a temporary tool to facilitate the transition
|
||||
from the deprecated ``user.message_set`` API and will be removed in Django 1.4
|
||||
according to Django's standard deprecation policy. For more information, see
|
||||
the full :doc:`release process documentation </internals/release-process>`.
|
||||
|
||||
In addition to the functionality in the ``FallbackStorage``, it adds a custom,
|
||||
read-only storage class that retrieves messages from the user ``Message``
|
||||
model. Any messages that were stored in the ``Message`` model (e.g., by code
|
||||
that has not yet been updated to use the messages framework) will be retrieved
|
||||
first, followed by those stored in a cookie and in the session, if any. Since
|
||||
messages stored in the ``Message`` model do not have a concept of levels, they
|
||||
will be assigned the ``INFO`` level by default.
|
||||
|
||||
Message levels
|
||||
--------------
|
||||
|
||||
|
@ -368,14 +341,13 @@ This sets the minimum message that will be saved in the message storage. See
|
|||
MESSAGE_STORAGE
|
||||
---------------
|
||||
|
||||
Default: ``'django.contrib.messages.storage.user_messages.LegacyFallbackStorage'``
|
||||
Default: ``'django.contrib.messages.storage.user_messages.FallbackStorage'``
|
||||
|
||||
Controls where Django stores message data. Valid values are:
|
||||
|
||||
* ``'django.contrib.messages.storage.fallback.FallbackStorage'``
|
||||
* ``'django.contrib.messages.storage.session.SessionStorage'``
|
||||
* ``'django.contrib.messages.storage.cookie.CookieStorage'``
|
||||
* ``'django.contrib.messages.storage.user_messages.LegacyFallbackStorage'``
|
||||
|
||||
See `Storage backends`_ for more details.
|
||||
|
||||
|
|
Loading…
Reference in New Issue