Django将模型名称作为参数传递给装饰器

时间:2011-07-17 13:21:09

标签: django decorator

我的应用程序中有以下装饰器,用于检查当前用户是否创建了任何位置对象,如果没有,则将其重定向到另一个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 个答案:

答案 0 :(得分:2)

绝对有可能。它可能最终看起来像以下(未经测试的)代码:

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

答案 1 :(得分:2)

Gareth将现有装饰器包装在处理模型类和重定向url的另一个函数中的方法是正确的。我建议进行以下小改动:

  • 使用exists()代替将count()与零
  • 进行比较
  • 使用django.utils.functional.wraps更新换行功能,而不是手动设置__name____doc__
  • wrap函数中的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