Reputation: 268225
I have written a decorator to display success message on object creation:
from django.contrib import messages
def success_message(klass):
def form_valid(self, form):
response = super(klass, self).form_valid(form)
messages.success(self.request, 'Object added successfully')
return response
klass.form_valid = form_valid
return klass
and use it to decorate class based generic view:
@success_message
class BandCreateView(CreateView):
model = Band
Now I want to parameterize the decorator so this is possible:
@success_message('Band created successfully.')
class BandCreateView(CreateView):
model = Band
How do I do it? I tried adding message
parameter to success_message
but the compiler complained about parameter count mismatch so I figure there must be another way.
Upvotes: 1
Views: 1223
Reputation: 11
I previously did this kind of thing as a mixin, but a decorator makes more sense (and is a bit less of a hassle to add to classes). You might like to get the form instance's verbose name like the following, to eliminate the need to pass a message to the decorator:
from django.utils.translation import ugettext_lazy as _
def ucfirst(value):
return value[0].upper() + value[1:]
def success_message(klass):
__orig_form_valid = klass.form_valid
def form_valid(self, form):
response = __orig_form_valid(self, form)
messages.success(self.request, _("%(object)s \"%(object_name)s\" was saved successfully.") %
{'object': ucfirst(form.instance._meta.verbose_name), 'object_name': unicode(form.instance)})
return response
klass.form_valid = form_valid
return klass
This will produce a success message something along the lines of:
Customer "ACME Inc." was saved successfully.
Upvotes: 1
Reputation: 6955
Looks like you have to use closure:
def decorator(arg):
def wrap(klass): ...
return wrap
because your call is evaluated to
class BandCreateView(CreateView): ...
BandCreateView = @success_message('Band created successfully.')(BandCreateView)
note double call
Upvotes: 3
Reputation: 268225
Parameterless decorator is declared as a class -> class
function.
Decorator with parameters is declared as a higher order args -> (class -> class)
function.
It takes parameters and returns a function that will take class and return class (i.e. the “simple” decorator).
An answer from a related thread by t.dubrownik literally saved me.
Anyway, the syntax for decorators with arguments is a bit different—the decorator with arguments should return a function that will take a function and return another function. So it should really return a normal decorator.
Although he is talking about function decorators, the concept is applicable to class decorators as well.
Here is what I got:
from django.contrib import messages
def success_message(message):
def actual_decorator(klass):
def form_valid(self, form):
response = super(klass, self).form_valid(form)
messages.success(self.request, message)
return response
klass.form_valid = form_valid
return klass
return actual_decorator
Note how actual_decorator
itself repeats the parameterless version of success_message
and how success_message
is now a higher order function because its return value is function itself.
Upvotes: 0