Django 模板过滤器上的装饰器?

发布于 2024-08-13 10:14:32 字数 1338 浏览 7 评论 0原文

我有一个模板过滤器,它执行非常简单的任务并且运行良好,但我想在其上使用装饰器。不幸的是,装饰器导致了一个令人讨厌的 django 错误,没有任何意义...

有效的代码:

@register.filter(name="has_network")
def has_network(profile, network):
    hasnetworkfunc = getattr(profile, "has_%s" % network)
    return hasnetworkfunc()

使用装饰器(不起作用):

@register.filter(name="has_network")
@cache_function(30)
def has_network(profile, network):
    hasnetworkfunc = getattr(profile, "has_%s" % network)
    return hasnetworkfunc()

这是错误:

模板语法错误 /

渲染时捕获异常: 从空列表中弹出

我尝试在装饰器内设置断点,并且我有相当的信心它甚至没有被调用...

但以防万一这里是装饰器(我知道有人会要求它)

我(暂时)用一个什么也不做的模拟装饰器替换了装饰器,但我仍然得到相同的错误

def cache_function(cache_timeout):
    def wrapper(fn):
        def decorator(*args, **kwargs):
            return fn(*args, **kwargs)
        return decorator
    return wrapper

编辑确认:这是因为装饰器需要*args**kwargs?我假设正在调用 pop() 来确保所有过滤器都至少接受一个参数?

将装饰器更改为此可以解决问题:

def cache_function(cache_timeout):
    def wrapper(fn):
        def decorator(arg1, arg2):
            return fn(arg1, arg2)
        return decorator
    return wrapper

不幸的是,这破坏了装饰器的通用性质:/现在该怎么办?

I have a template filter that performs a very simple task and works well, but I would like to use a decorator on it. Unfortunately the decorator causes a nasty django error that doesn't make any sense...

Code that works:

@register.filter(name="has_network")
def has_network(profile, network):
    hasnetworkfunc = getattr(profile, "has_%s" % network)
    return hasnetworkfunc()

With Decorator (doesn't work):

@register.filter(name="has_network")
@cache_function(30)
def has_network(profile, network):
    hasnetworkfunc = getattr(profile, "has_%s" % network)
    return hasnetworkfunc()

Here is the error:

TemplateSyntaxError at /

Caught an exception while rendering:
pop from empty list

I have tried setting break points inside the decorator and I am reasonably confident that it is not even being called...

But just in case here is the decorator (I know someone will ask for it)

I replaced the decorator (temporarily) with a mock decorator that does nothing, but I still get the same error

def cache_function(cache_timeout):
    def wrapper(fn):
        def decorator(*args, **kwargs):
            return fn(*args, **kwargs)
        return decorator
    return wrapper

edit CONFIRMED: It is caused because the decorator takes *args and **kwargs? I assume pop() is being called to ensure filters all take at least one arg?

changing the decorator to this fixes the problem:

def cache_function(cache_timeout):
    def wrapper(fn):
        def decorator(arg1, arg2):
            return fn(arg1, arg2)
        return decorator
    return wrapper

Unfortunately that ruins the generic nature of the decorator :/ what to do now?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

枕花眠 2024-08-20 10:14:32

最终答案:向装饰器添加一个额外的参数,指示正在装饰的内容

可能有更优雅的东西,但这是可行的。

from django.core.cache import cache
from django.db.models.query import QuerySet
try:
    from cPickle import dumps
except:
    from pickle import dumps
from hashlib import sha1

cache_miss = object()

class CantPickleAQuerySet(Exception): pass

def cache_function(cache_timeout, func_type='generic'):
    def wrapper(fn):
        def decorator(*args, **kwargs):
            try:
                cache_indentifiers = "%s%s%s%s" % (
                                         fn.__module__,
                                         fn.__name__,
                                         dumps(args),
                                         dumps(kwargs)
                                         )
            except Exception, e:
                print "Error: %s\nFailed to generate cache key: %s%s" % (e, fn.__module__, fn.__name__)
                return fn(*args, **kwargs)

            cache_key = sha1(cache_indentifiers).hexdigest()

            value = cache.get(cache_key, cache_miss)

            if value is cache_miss:
                value = fn(*args, **kwargs)

                if isinstance(value, QuerySet):
                    raise CantPickleAQuerySet("You can't cache a queryset. But you CAN cache a list! just convert your Queryset (the value you were returning) to a list like so `return list(queryset)`")

                try:
                    cache.set(cache_key, value, cache_timeout)
                except Exception, e:
                    print "Error: %s\nFailed to cache: %s\nvalue: %s" % (e, cache_indentifiers, value)

            return value

        no_arg2 = object()
        def filter_decorator(arg1, arg2=no_arg2):
            if arg2 is no_arg2:
                return decorator(arg1)
            else:
                return decorator(arg1, arg2)

        if func_type == 'generic':
            return decorator

        elif func_type == 'filter':
            return filter_decorator

    return wrapper

Final Answer: Add an extra argument to the decorator indicating what is being decorated

There may be something more elegant, but this works.

from django.core.cache import cache
from django.db.models.query import QuerySet
try:
    from cPickle import dumps
except:
    from pickle import dumps
from hashlib import sha1

cache_miss = object()

class CantPickleAQuerySet(Exception): pass

def cache_function(cache_timeout, func_type='generic'):
    def wrapper(fn):
        def decorator(*args, **kwargs):
            try:
                cache_indentifiers = "%s%s%s%s" % (
                                         fn.__module__,
                                         fn.__name__,
                                         dumps(args),
                                         dumps(kwargs)
                                         )
            except Exception, e:
                print "Error: %s\nFailed to generate cache key: %s%s" % (e, fn.__module__, fn.__name__)
                return fn(*args, **kwargs)

            cache_key = sha1(cache_indentifiers).hexdigest()

            value = cache.get(cache_key, cache_miss)

            if value is cache_miss:
                value = fn(*args, **kwargs)

                if isinstance(value, QuerySet):
                    raise CantPickleAQuerySet("You can't cache a queryset. But you CAN cache a list! just convert your Queryset (the value you were returning) to a list like so `return list(queryset)`")

                try:
                    cache.set(cache_key, value, cache_timeout)
                except Exception, e:
                    print "Error: %s\nFailed to cache: %s\nvalue: %s" % (e, cache_indentifiers, value)

            return value

        no_arg2 = object()
        def filter_decorator(arg1, arg2=no_arg2):
            if arg2 is no_arg2:
                return decorator(arg1)
            else:
                return decorator(arg1, arg2)

        if func_type == 'generic':
            return decorator

        elif func_type == 'filter':
            return filter_decorator

    return wrapper
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文