Fixed #18658 -- Improved ModelAdmin.message_user API
Thanks to Lowe Thiderman for the patch and tests
This commit is contained in:
parent
8b659e439b
commit
edf7ad36fa
1
AUTHORS
1
AUTHORS
|
@ -531,6 +531,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Terry Huang <terryh.tp@gmail.com>
|
Terry Huang <terryh.tp@gmail.com>
|
||||||
Travis Terry <tdterry7@gmail.com>
|
Travis Terry <tdterry7@gmail.com>
|
||||||
thebjorn <bp@datakortet.no>
|
thebjorn <bp@datakortet.no>
|
||||||
|
Lowe Thiderman <lowe.thiderman@gmail.com>
|
||||||
Zach Thompson <zthompson47@gmail.com>
|
Zach Thompson <zthompson47@gmail.com>
|
||||||
Michael Thornhill <michael.thornhill@gmail.com>
|
Michael Thornhill <michael.thornhill@gmail.com>
|
||||||
Deepak Thukral <deep.thukral@gmail.com>
|
Deepak Thukral <deep.thukral@gmail.com>
|
||||||
|
|
|
@ -691,12 +691,30 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
change_message = ' '.join(change_message)
|
change_message = ' '.join(change_message)
|
||||||
return change_message or _('No fields changed.')
|
return change_message or _('No fields changed.')
|
||||||
|
|
||||||
def message_user(self, request, message):
|
def message_user(self, request, message, level=messages.INFO, extra_tags='',
|
||||||
|
fail_silently=False):
|
||||||
"""
|
"""
|
||||||
Send a message to the user. The default implementation
|
Send a message to the user. The default implementation
|
||||||
posts a message using the django.contrib.messages backend.
|
posts a message using the django.contrib.messages backend.
|
||||||
|
|
||||||
|
Exposes almost the same API as messages.add_message(), but accepts the
|
||||||
|
positional arguments in a different order to maintain backwards
|
||||||
|
compatibility. For convenience, it accepts the `level` argument as
|
||||||
|
a string rather than the ususal level number.
|
||||||
"""
|
"""
|
||||||
messages.info(request, message)
|
|
||||||
|
if not isinstance(level, int):
|
||||||
|
# attempt to get the level if passed a string
|
||||||
|
try:
|
||||||
|
level = getattr(messages.constants, level.upper())
|
||||||
|
except AttributeError:
|
||||||
|
levels = messages.constants.DEFAULT_TAGS.values()
|
||||||
|
levels_repr = ', '.join('`%s`' % l for l in levels)
|
||||||
|
raise ValueError('Bad message level string: `%s`. '
|
||||||
|
'Possible values are: %s' % (level, levels_repr))
|
||||||
|
|
||||||
|
messages.add_message(request, level, message, extra_tags=extra_tags,
|
||||||
|
fail_silently=fail_silently)
|
||||||
|
|
||||||
def save_form(self, request, form, change):
|
def save_form(self, request, form, change):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -140,6 +140,15 @@ That's really all there is to it! If you're itching to write your own actions,
|
||||||
you now know enough to get started. The rest of this document just covers more
|
you now know enough to get started. The rest of this document just covers more
|
||||||
advanced techniques.
|
advanced techniques.
|
||||||
|
|
||||||
|
Handling errors in actions
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
If there are foreseeable error conditions that may occur while running your
|
||||||
|
action, you should gracefully inform the user of the problem. This means
|
||||||
|
handling exceptions and and using
|
||||||
|
:meth:`django.contrib.admin.ModelAdmin.message_user` to display a user friendly
|
||||||
|
description of the problem in the response.
|
||||||
|
|
||||||
Advanced action techniques
|
Advanced action techniques
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
|
|
@ -1303,11 +1303,19 @@ templates used by the :class:`ModelAdmin` views:
|
||||||
return qs
|
return qs
|
||||||
return qs.filter(author=request.user)
|
return qs.filter(author=request.user)
|
||||||
|
|
||||||
.. method:: ModelAdmin.message_user(request, message)
|
.. method:: ModelAdmin.message_user(request, message, level=messages.INFO, extra_tags='', fail_silently=False)
|
||||||
|
|
||||||
Sends a message to the user. The default implementation creates a message
|
Sends a message to the user using the :mod:`django.contrib.messages`
|
||||||
using the :mod:`django.contrib.messages` backend. See the
|
backend. See the :ref:`custom ModelAdmin example <custom-admin-action>`.
|
||||||
:ref:`custom ModelAdmin example <custom-admin-action>`.
|
|
||||||
|
.. versionadded:: 1.5
|
||||||
|
|
||||||
|
Keyword arguments allow you to change the message level, add extra CSS
|
||||||
|
tags, or fail silently if the ``contrib.messages`` framework is not
|
||||||
|
installed. These keyword arguments match those for
|
||||||
|
:func:`django.contrib.messages.add_message`, see that function's
|
||||||
|
documentation for more details. One difference is that the level may be
|
||||||
|
passed as a string label in addition to integer/constant.
|
||||||
|
|
||||||
.. method:: ModelAdmin.get_paginator(queryset, per_page, orphans=0, allow_empty_first_page=True)
|
.. method:: ModelAdmin.get_paginator(queryset, per_page, orphans=0, allow_empty_first_page=True)
|
||||||
|
|
||||||
|
|
|
@ -313,6 +313,11 @@ Django 1.5 also includes several smaller improvements worth noting:
|
||||||
DeprecationWarnings should be printed to the console in development
|
DeprecationWarnings should be printed to the console in development
|
||||||
environments the way they have been in Python versions < 2.7.
|
environments the way they have been in Python versions < 2.7.
|
||||||
|
|
||||||
|
* The API for :meth:`django.contrib.admin.ModelAdmin.message_user` method has
|
||||||
|
been modified to accept additional arguments adding capabilities similar to
|
||||||
|
:func:`django.contrib.messages.add_message`. This is useful for generating
|
||||||
|
error messages from admin actions.
|
||||||
|
|
||||||
Backwards incompatible changes in 1.5
|
Backwards incompatible changes in 1.5
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ from .models import (Article, Chapter, Account, Media, Child, Parent, Picture,
|
||||||
Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug,
|
Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug,
|
||||||
AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod,
|
AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod,
|
||||||
AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated,
|
AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated,
|
||||||
RelatedPrepopulated, UndeletableObject, Simple)
|
RelatedPrepopulated, UndeletableObject, UserMessenger, Simple)
|
||||||
|
|
||||||
|
|
||||||
def callable_year(dt_value):
|
def callable_year(dt_value):
|
||||||
|
@ -592,6 +592,28 @@ def callable_on_unknown(obj):
|
||||||
class AttributeErrorRaisingAdmin(admin.ModelAdmin):
|
class AttributeErrorRaisingAdmin(admin.ModelAdmin):
|
||||||
list_display = [callable_on_unknown, ]
|
list_display = [callable_on_unknown, ]
|
||||||
|
|
||||||
|
class MessageTestingAdmin(admin.ModelAdmin):
|
||||||
|
actions = ["message_debug", "message_info", "message_success",
|
||||||
|
"message_warning", "message_error", "message_extra_tags"]
|
||||||
|
|
||||||
|
def message_debug(self, request, selected):
|
||||||
|
self.message_user(request, "Test debug", level="debug")
|
||||||
|
|
||||||
|
def message_info(self, request, selected):
|
||||||
|
self.message_user(request, "Test info", level="info")
|
||||||
|
|
||||||
|
def message_success(self, request, selected):
|
||||||
|
self.message_user(request, "Test success", level="success")
|
||||||
|
|
||||||
|
def message_warning(self, request, selected):
|
||||||
|
self.message_user(request, "Test warning", level="warning")
|
||||||
|
|
||||||
|
def message_error(self, request, selected):
|
||||||
|
self.message_user(request, "Test error", level="error")
|
||||||
|
|
||||||
|
def message_extra_tags(self, request, selected):
|
||||||
|
self.message_user(request, "Test tags", extra_tags="extra_tag")
|
||||||
|
|
||||||
|
|
||||||
site = admin.AdminSite(name="admin")
|
site = admin.AdminSite(name="admin")
|
||||||
site.register(Article, ArticleAdmin)
|
site.register(Article, ArticleAdmin)
|
||||||
|
@ -667,6 +689,7 @@ site.register(AdminOrderedAdminMethod, AdminOrderedAdminMethodAdmin)
|
||||||
site.register(AdminOrderedCallable, AdminOrderedCallableAdmin)
|
site.register(AdminOrderedCallable, AdminOrderedCallableAdmin)
|
||||||
site.register(Color2, CustomTemplateFilterColorAdmin)
|
site.register(Color2, CustomTemplateFilterColorAdmin)
|
||||||
site.register(Simple, AttributeErrorRaisingAdmin)
|
site.register(Simple, AttributeErrorRaisingAdmin)
|
||||||
|
site.register(UserMessenger, MessageTestingAdmin)
|
||||||
|
|
||||||
# Register core models we need in our tests
|
# Register core models we need in our tests
|
||||||
from django.contrib.auth.models import User, Group
|
from django.contrib.auth.models import User, Group
|
||||||
|
|
|
@ -651,6 +651,10 @@ class UndeletableObject(models.Model):
|
||||||
"""
|
"""
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
class UserMessenger(models.Model):
|
||||||
|
"""
|
||||||
|
Dummy class for testing message_user functions on ModelAdmin
|
||||||
|
"""
|
||||||
|
|
||||||
class Simple(models.Model):
|
class Simple(models.Model):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -3697,3 +3697,61 @@ class AdminViewLogoutTest(TestCase):
|
||||||
self.assertEqual(response.template_name, 'admin/login.html')
|
self.assertEqual(response.template_name, 'admin/login.html')
|
||||||
self.assertEqual(response.request['PATH_INFO'], '/test_admin/admin/')
|
self.assertEqual(response.request['PATH_INFO'], '/test_admin/admin/')
|
||||||
self.assertContains(response, '<input type="hidden" name="next" value="/test_admin/admin/" />')
|
self.assertContains(response, '<input type="hidden" name="next" value="/test_admin/admin/" />')
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
||||||
|
class AdminUserMessageTest(TestCase):
|
||||||
|
urls = "regressiontests.admin_views.urls"
|
||||||
|
fixtures = ['admin-views-users.xml']
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.client.login(username='super', password='secret')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.client.logout()
|
||||||
|
|
||||||
|
def send_message(self, level):
|
||||||
|
"""
|
||||||
|
Helper that sends a post to the dummy test methods and asserts that a
|
||||||
|
message with the level has appeared in the response.
|
||||||
|
"""
|
||||||
|
action_data = {
|
||||||
|
ACTION_CHECKBOX_NAME: [1],
|
||||||
|
'action': 'message_%s' % level,
|
||||||
|
'index': 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post('/test_admin/admin/admin_views/usermessenger/',
|
||||||
|
action_data, follow=True)
|
||||||
|
self.assertContains(response,
|
||||||
|
'<li class="%s">Test %s</li>' % (level, level),
|
||||||
|
html=True)
|
||||||
|
|
||||||
|
@override_settings(MESSAGE_LEVEL=10) # Set to DEBUG for this request
|
||||||
|
def test_message_debug(self):
|
||||||
|
self.send_message('debug')
|
||||||
|
|
||||||
|
def test_message_info(self):
|
||||||
|
self.send_message('info')
|
||||||
|
|
||||||
|
def test_message_success(self):
|
||||||
|
self.send_message('success')
|
||||||
|
|
||||||
|
def test_message_warning(self):
|
||||||
|
self.send_message('warning')
|
||||||
|
|
||||||
|
def test_message_error(self):
|
||||||
|
self.send_message('error')
|
||||||
|
|
||||||
|
def test_message_extra_tags(self):
|
||||||
|
action_data = {
|
||||||
|
ACTION_CHECKBOX_NAME: [1],
|
||||||
|
'action': 'message_extra_tags',
|
||||||
|
'index': 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post('/test_admin/admin/admin_views/usermessenger/',
|
||||||
|
action_data, follow=True)
|
||||||
|
self.assertContains(response,
|
||||||
|
'<li class="extra_tag info">Test tags</li>',
|
||||||
|
html=True)
|
||||||
|
|
Loading…
Reference in New Issue