Декораторы на шаблонных фильтрах Django? - PullRequest
1 голос
/ 04 декабря 2009

У меня есть шаблонный фильтр, который выполняет очень простую задачу и работает хорошо, но я бы хотел использовать для этого декоратор. К сожалению, декоратор вызывает неприятную ошибку 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()

Вот ошибка:

TemplateSyntaxError at /

Выявлено исключение при рендеринге: выскочить из пустого списка

Я попытался установить точки останова внутри декоратора, и я достаточно уверен, что он даже не вызывается ...

Но на всякий случай вот декоратор (я знаю, кто-то попросит об этом)

Я заменил декоратор (временно) на фиктивный декоратор, который ничего не делает, но все равно получаю ту же ошибку

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

edit ПОДТВЕРЖДЕНО : Это вызвано тем, что декоратор принимает *args и **kwargs? Я предполагаю, что pop() вызывается, чтобы гарантировать, что все фильтры принимают хотя бы один аргумент?

Смена декоратора для решения этой проблемы:

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

К сожалению, это разрушает общий характер декоратора: / что теперь делать?

1 Ответ

0 голосов
/ 04 декабря 2009

Окончательный ответ: добавьте дополнительный аргумент декоратору, указывающий, что украшается

Может быть что-то более элегантное, но это работает.

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
...