From 9a85ad89c20e88340cdba85b8e9f5806c95835db Mon Sep 17 00:00:00 2001 From: Paul Collins Date: Mon, 18 Mar 2013 15:48:47 -0700 Subject: [PATCH] Fixed #16319 -- added SuccessMessageMixin to contrib.messages Thanks martinogden for the initial patch and d1ffuz0r for tests. --- AUTHORS | 1 + django/contrib/messages/tests/__init__.py | 1 + django/contrib/messages/tests/mixins.py | 14 +++++++ django/contrib/messages/tests/urls.py | 18 ++++++++- django/contrib/messages/views.py | 19 +++++++++ docs/ref/contrib/messages.txt | 47 +++++++++++++++++++++++ docs/releases/1.6.txt | 4 ++ tests/generic_views/views.py | 1 + 8 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 django/contrib/messages/tests/mixins.py create mode 100644 django/contrib/messages/views.py diff --git a/AUTHORS b/AUTHORS index 4341a05d9b7..2790963abe3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -607,6 +607,7 @@ answer newbie questions, and generally made Django that much better: Cheng Zhang Hannes Struß Deric Crago + Paul Collins A big THANK YOU goes to: diff --git a/django/contrib/messages/tests/__init__.py b/django/contrib/messages/tests/__init__.py index f3f6b653d0a..25a0f9aa257 100644 --- a/django/contrib/messages/tests/__init__.py +++ b/django/contrib/messages/tests/__init__.py @@ -2,3 +2,4 @@ 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.mixins import SuccessMessageMixinTests diff --git a/django/contrib/messages/tests/mixins.py b/django/contrib/messages/tests/mixins.py new file mode 100644 index 00000000000..8eef4cb3dcf --- /dev/null +++ b/django/contrib/messages/tests/mixins.py @@ -0,0 +1,14 @@ +from django.test.testcases import TestCase +from django.contrib.messages.tests.urls import ContactFormViewWithMsg +from django.core.urlresolvers import reverse + +class SuccessMessageMixinTests(TestCase): + urls = 'django.contrib.messages.tests.urls' + + def test_set_messages_success(self): + author = {'name': 'John Doe', + 'slug': 'success-msg'} + add_url = reverse('add_success_msg') + req = self.client.post(add_url, author) + self.assertIn(ContactFormViewWithMsg.success_message % author, + req.cookies['messages'].value) diff --git a/django/contrib/messages/tests/urls.py b/django/contrib/messages/tests/urls.py index 6d32a614eb0..0541b5a336e 100644 --- a/django/contrib/messages/tests/urls.py +++ b/django/contrib/messages/tests/urls.py @@ -1,10 +1,13 @@ -from django.conf.urls import patterns +from django.conf.urls import patterns, url from django.contrib import messages from django.core.urlresolvers import reverse +from django import forms from django.http import HttpResponseRedirect, HttpResponse from django.template import RequestContext, Template from django.template.response import TemplateResponse from django.views.decorators.cache import never_cache +from django.contrib.messages.views import SuccessMessageMixin +from django.views.generic.edit import FormView TEMPLATE = """{% if messages %}
    @@ -49,8 +52,21 @@ def show(request): def show_template_response(request): return TemplateResponse(request, Template(TEMPLATE)) + +class ContactForm(forms.Form): + name = forms.CharField(required=True) + slug = forms.SlugField(required=True) + + +class ContactFormViewWithMsg(SuccessMessageMixin, FormView): + form_class = ContactForm + success_url = show + success_message = "%(name)s was created successfully" + + urlpatterns = patterns('', ('^add/(debug|info|success|warning|error)/$', add), + url('^add/msg/$', ContactFormViewWithMsg.as_view(), name='add_success_msg'), ('^show/$', show), ('^template_response/add/(debug|info|success|warning|error)/$', add_template_response), ('^template_response/show/$', show_template_response), diff --git a/django/contrib/messages/views.py b/django/contrib/messages/views.py new file mode 100644 index 00000000000..d08804aa1d0 --- /dev/null +++ b/django/contrib/messages/views.py @@ -0,0 +1,19 @@ +from django.views.generic.edit import FormMixin +from django.contrib import messages + + +class SuccessMessageMixin(FormMixin): + """ + Adds a success message on successful form submission. + """ + success_message = '' + + def form_valid(self, form): + response = super(SuccessMessageMixin, self).form_valid(form) + success_message = self.get_success_message(form.cleaned_data) + if success_message: + messages.success(self.request, success_message) + return response + + def get_success_message(self, cleaned_data): + return self.success_message % cleaned_data diff --git a/docs/ref/contrib/messages.txt b/docs/ref/contrib/messages.txt index dd7c8dbd650..0dd732bec2c 100644 --- a/docs/ref/contrib/messages.txt +++ b/docs/ref/contrib/messages.txt @@ -286,6 +286,53 @@ example:: use one of the ``add_message`` family of methods. It does not hide failures that may occur for other reasons. +Adding messages in Class Based Views +------------------------------------ + +.. versionadded:: 1.6 + +.. class:: django.contrib.messages.views.SuccessMessageMixin + + Adds a success message attribute to + :class:`~django.views.generic.edit.FormView` based classes + + .. method:: get_success_message(cleaned_data) + + ``cleaned_data`` is the cleaned data from the form which is used for + string formatting + +**Example views.py**:: + + from django.contrib.messages.views import SuccessMessageMixin + from django.views.generic.edit import CreateView + from myapp.models import Author + + class AuthorCreate(SuccessMessageMixin, CreateView): + model = Author + success_url = '/success/' + success_message = "%(name)s was created successfully" + +The cleaned data from the ``form`` is available for string interpolation using +the ``%(field_name)s`` syntax. For ModelForms, if you need access to fields +from the saved ``object`` override the +:meth:`~django.contrib.messages.views.SuccessMessageMixin.get_success_message` +method. + +**Example views.py for ModelForms**:: + + from django.contrib.messages.views import SuccessMessageMixin + from django.views.generic.edit import CreateView + from myapp.models import ComplicatedModel + + class ComplicatedCreate(SuccessMessageMixin, CreateView): + model = ComplicatedModel + success_url = '/success/' + success_message = "%(calculated_field)s was created successfully" + + def get_success_message(self, cleaned_data): + return self.success_message % dict(cleaned_data, + calculated_field=self.object.calculated_field) + Expiration of messages ====================== diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index 959a5e3ef0c..3e6c3b8bfb5 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -126,6 +126,10 @@ Minor features * The ``MemcachedCache`` cache backend now uses the latest :mod:`pickle` protocol available. +* Added :class:`~django.contrib.messages.views.SuccessMessageMixin` which + provides a ``success_message`` attribute for + :class:`~django.view.generic.edit.FormView` based classes. + * Added the :attr:`django.db.models.ForeignKey.db_constraint` and :attr:`django.db.models.ManyToManyField.db_constraint` options. diff --git a/tests/generic_views/views.py b/tests/generic_views/views.py index 71e78e82c77..856565a953d 100644 --- a/tests/generic_views/views.py +++ b/tests/generic_views/views.py @@ -1,6 +1,7 @@ from __future__ import absolute_import from django.contrib.auth.decorators import login_required +from django.contrib.messages.views import SuccessMessageMixin from django.core.paginator import Paginator from django.core.urlresolvers import reverse, reverse_lazy from django.utils.decorators import method_decorator