Как добавить параметры в декораторы универсального представления на основе классов Django? - PullRequest
1 голос
/ 29 сентября 2011

Я написал декоратор для отображения сообщения об успешном создании объекта:

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

и использовать его для украшения общего представления на основе классов:

@success_message
class BandCreateView(CreateView):
    model = Band

Теперь я хочу параметризовать декоратор, так что это возможно:

@success_message('Band created successfully.')
class BandCreateView(CreateView):
    model = Band

Как мне это сделать? Я попытался добавить параметр message в success_message, но компилятор пожаловался на несоответствие количества параметров, поэтому я считаю, что должен быть другой способ.

Ответы [ 3 ]

3 голосов
/ 29 сентября 2011

Похоже, вы должны использовать замыкание:

def decorator(arg):
    def wrap(klass): ...
    return wrap

, потому что ваш вызов оценивается как

class BandCreateView(CreateView): ...
BandCreateView = @success_message('Band created successfully.')(BandCreateView)

note double call

1 голос
/ 28 февраля 2012

Раньше я делал такие вещи как миксин, но декоратор имеет больше смысла (и это немного меньше хлопот, чтобы добавить к классам). Возможно, вы захотите получить подробное имя экземпляра формы, как показано ниже, чтобы избежать необходимости передавать сообщение декоратору:

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

Это приведет к сообщению об успехе, например:

Заказчик "ACME Inc." был успешно сохранен.

0 голосов
/ 29 сентября 2011

TL; DR

Декоратор без параметров объявлен как функция class -> class.
Декоратор с параметрами объявлен как более высокий порядок args -> (class -> class)function.

Он принимает параметры и возвращает функцию, которая примет класс и вернет класс (т. е. «простой» декоратор).

Пояснение

Ответ от связанной темы от t.dubrownik буквально спас меня.

В любом случае, синтаксис для декораторов с аргументами немного отличается - декоратор с аргументами должен возвращать функцию, которая примет функцию и вернет другую функцию .Так что действительно должен вернуть нормальный декоратор.

Хотя он говорит о декораторах функций, эта концепция применима и к декораторам классов.
Вот что я получил:

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

Обратите внимание, как actual_decoratorСам по себе повторяет безпараметрическую версию success_message, и теперь success_message является функцией более высокого порядка, поскольку ее возвращаемое значение является самой функцией.

...