Django передает имя модели в качестве параметра декоратору - PullRequest
1 голос
/ 17 июля 2011

В моем приложении есть следующий декоратор, который проверяет, создал ли текущий пользователь какие-либо объекты Location и перенаправляет их на другой URL, если они этого не сделали.

def location_required(f):
    def wrap(request, *args, **kwargs):
        locations = Location.objects.filter(user=request.user)
        if locations.count() == 0:
            return HttpResponseRedirect("/")
        return f(request, *args, **kwargs)
    wrap.__doc__=f.__doc__
    wrap.__name__=f.__name__
    return wrap   

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

Возможно ли это и какие изменения мне нужно внести?

Любой совет будет принят с благодарностью.

Спасибо.

Ответы [ 2 ]

2 голосов
/ 17 июля 2011

Подход Гарета оборачивания существующего декоратора в другую функцию, которая обрабатывает класс модели и URL-адрес перенаправления, верен.Я предлагаю следующие небольшие изменения:

  • Использование exists() вместо сравнения count() с нулем
  • Использование django.utils.functional.wraps для обновления функции обтекания вместо установки __name__ и__doc__ вручную.
  • Переменная locations в функции обтекания названа неправильно, теперь это может быть экземпляр любой модели.

Это дает:

from django.utils.functional import wraps

def object_required(model_class, redirect_url="/"):
    # model_class and redirect_url are available to all inner functions
    def decorator(f):
        # this is called with f, the function being decorated
        def wrapper(request, *args, **kwargs):
            # this is called each time the real function is executed
            instances = model_class.objects.filter(user=request.user)
            if not instances.exists():
                return HttpResponseRedirect(redirect_url)
            return f(request, *args, **kwargs)
        return wraps(f)(wrapper)
    return decorator

В представлении:

@object_required(Location, "/")
def my_view_function(request):
    # your view code
2 голосов
/ 17 июля 2011

Определенно возможно. В конечном итоге это может выглядеть примерно так (непроверенный) код:

def object_required(model_class, redirect_url):
    def location_required(f):
        def wrap(request, *args, **kwargs):
            locations = model_class.objects.filter(user=request.user)
            if locations.count() == 0:
                return HttpResponseRedirect(redirect_url)
            return f(request, *args, **kwargs)
        wrap.__doc__=f.__doc__
        wrap.__name__=f.__name__
        return wrap
    return location_required

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

@object_required(Location, '/')
def my_view_func(request)
    #your view code
...