Добавление аргумента в декоратор - PullRequest
5 голосов
/ 05 декабря 2009

У меня есть этот декоратор, используемый для украшения представления django, когда я не хочу, чтобы представление выполнялось, если аргумент share равен True (обрабатывается промежуточным ПО)

class no_share(object):
    def __init__(self, view):
        self.view = view

    def __call__(self, request, *args, **kwargs):
        """Don't let them in if it's shared"""

        if kwargs.get('shared', True):
            from django.http import Http404
            raise Http404('not availiable for sharing')

        return self.view(request, *args, **kwargs)

В настоящее время работает так:

@no_share
def prefs(request, [...])

Но я хочу немного расширить функциональность, чтобы она работала так:

@no_share('prefs')
def prefs(request, [...])

У меня вопрос, как я могу изменить этот класс декоратора, чтобы он принимал дополнительные аргументы?

Ответы [ 6 ]

7 голосов
/ 05 декабря 2009

Надеюсь эта статья Брюса Экеля поможет.

Upd: Согласно статье ваш код будет выглядеть так:

class no_share(object):
    def __init__(self, arg1):
        self.arg1 = arg1

    def __call__(self, f):
        """Don't let them in if it's shared"""

        # Do something with the argument passed to the decorator.
        print 'Decorator arguments:', self.arg1

        def wrapped_f(request, *args, **kwargs):
            if kwargs.get('shared', True):
                from django.http import Http404
                raise Http404('not availiable for sharing')
            f(request, *args, **kwargs)            
        return wrapped_f

для использования по желанию:

@no_share('prefs')
def prefs(request, [...])
4 голосов
/ 05 декабря 2009

Статья Брюса Экеля , о которой упоминал Li0liQ, должна помочь выяснить это. Декораторы с аргументами и без них ведут себя немного иначе. Большая разница в том, что когда вы передаете аргументы, метод __call__ вызывается один раз для __init__, и он должен возвращать функцию, которая будет вызываться всякий раз, когда вызывается декорированная функция. Когда аргументов нет, метод __call__ вызывается каждый раз, когда вызывается декорированная функция.

Что это значит для вас? Способ вызова __init__ и __call__ для @no_arg_decorator отличается от вызова для @decorator('with','args').

Вот два декоратора, которые могут помочь вам. Вы можете использовать только декоратор @no_share_on (...), если всегда используете его в скобках.

def sharing_check(view, attr_name, request, *args, **kwargs):
    if kwargs.get(attr_name, True):
        from django.http import Http404
        raise Http404('not availiable for sharing')

    return view(request, *args, **kwargs)

class no_share(object):
    """A decorator w/o arguments.  Usage:
    @no_share
    def f(request):
        ...
    """
    def __init__(self, view):
        self.view = view

    def __call__(self, request, *args, **kwargs):
        return sharing_check(self.view, 'sharing', request, *args, **kwargs)

class no_share_on(object):
    """A decorator w/ arguments.  Usage:
    @no_share_on('something')
    def f(request):
        ...
    --OR--
    @no_share_on()
    def g(request):
        ...
    """
    def __init__(self, attr_name='sharing'):
        self.attr_name = attr_name

    def  __call__(self, view):
        def wrapper_f(request, *args, **kwargs):
            return sharing_check(view, self.attr_name, request, *args, **kwargs)
1 голос
/ 05 декабря 2009

Я думаю, что закрытие может работать здесь.

def no_share(attr):
    def _no_share(decorated):
        def func(self, request, *args, **kwargs):
            """Don't let them in if it's shared"""

            if kwargs.get(attr, True):
                from django.http import Http404
                raise Http404('not availiable for sharing')

            return decorated(request, *args, **kwargs)
        return func
    return _no_share
1 голос
/ 05 декабря 2009
class no_share(object):
    def __init__(self, foo, view):
        self.foo = foo
        self.view = view
0 голосов
/ 03 февраля 2012

Я знаю, что это немного поздно ... но я не видел упоминаний об этом способе ведения дел (вероятно, потому что его не было, когда задавался вопрос), но в интересах полноты Мне показалось полезным посмотреть, как Django сам реализовал такую ​​вещь. Посмотрите на:

django.views.decorators.http.require_http_methods https://github.com/django/django/blob/master/django/views/decorators/http.py

from functools import wraps
from django.utils.decorators import decorator_from_middleware, available_attrs

def require_http_methods(request_method_list):
    """
    Decorator to make a view only accept particular request methods. Usage::

    @require_http_methods(["GET", "POST"])
    def my_view(request):
    # I can assume now that only GET or POST requests make it this far
    # ...

    Note that request methods should be in uppercase.
    """
    def decorator(func):
        @wraps(func, assigned=available_attrs(func))
        def inner(request, *args, **kwargs):
            # .. do stuff here

            return func(request, *args, **kwargs)
        return inner
    return decorator
0 голосов
/ 05 декабря 2009

Поскольку кажется, что вы где-то ошибаетесь, вот более полный пример, который может помочь вам понять, что вы делаете неправильно. Использование этого в качестве раскрывающегося окна должно работать.

class no_share(object):
    def __init__(self, view, attr_name):
        self.view = view
        self.attr_name = attr_name

    def __call__(self, request, *args, **kwargs):
        """Don't let them in if it's shared"""

        if kwargs.get(self.attr_name, True):
            from django.http import Http404
            raise Http404('not availiable for sharing')

        return self.view(request, *args, **kwargs)
...