Django 自定义装饰器 - 函数没有属性 get

Posted

技术标签:

【中文标题】Django 自定义装饰器 - 函数没有属性 get【英文标题】:Django custom decorator - function has no attribute get 【发布时间】:2018-05-20 16:10:48 【问题描述】:

我正在尝试基于@cache_page 装饰器创建自己的装饰器。我的装饰器应该像@cache_page 一样工作,除非视图的slug 属性与request.user.userprofile slug 匹配,然后正常处理视图并返回未缓存的响应

伪代码:

def profile(request,slug):
    # if not request.user = User.objects.get(userprofile__slug=slug): 
    # return cache
    # else compute response and return it

我的装饰师:

def exclude_cache_for_request_user(*args, **kwargs):
    def exclude_cache_for_request_user_decorator(func):
        def func_wrapper(*fargs,**fkwargs):
            request = fargs[0]
            if request:
                user = getattr(request,'user',None)
                owner_slug = fkwargs.get('slug')
                owner = User.objects.get(userprofile__slug=owner_slug)
                if user==owner:
                    return func(*fargs, **fkwargs)
                else:
                    if len(args) != 1 or callable(args[0]):
                        raise TypeError("cache_page has a single mandatory positional argument: timeout")
                    cache_timeout = args[0]
                    cache_alias = kwargs.pop('cache', None)
                    key_prefix = kwargs.pop('key_prefix', None)
                    if kwargs:
                        raise TypeError("cache_page has two optional keyword arguments: cache and key_prefix")

                    return decorator_from_middleware_with_args(CacheMiddleware)(
                        cache_timeout=cache_timeout, cache_alias=cache_alias, key_prefix=key_prefix
                    )
        return func_wrapper
    return exclude_cache_for_request_user_decorator

如果用户匹配 slug,这将有效。否则,它会引发:

异常值: 'function' 对象没有属性 'get'

完整的追溯:

File "/home/milano/.virtualenvs/beduenovenv/local/lib/python2.7/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/home/milano/.virtualenvs/beduenovenv/local/lib/python2.7/site-packages/django/utils/deprecation.py" in __call__
  142.             response = self.process_response(request, response)

File "/home/milano/.virtualenvs/beduenovenv/local/lib/python2.7/site-packages/django/middleware/clickjacking.py" in process_response
  32.         if response.get('X-Frame-Options') is not None:

Exception Type: AttributeError at /profiles/detail/cingo/
Exception Value: 'function' object has no attribute 'get'

你知道问题出在哪里吗?最好的情况是分别为用户和其他用户缓存。所以这个视图有 2 个缓存。

编辑:这是一个原创的 @cache_page 装饰器

def cache_page(*args, **kwargs):
    """
    Decorator for views that tries getting the page from the cache and
    populates the cache if the page isn't in the cache yet.

    The cache is keyed by the URL and some data from the headers.
    Additionally there is the key prefix that is used to distinguish different
    cache areas in a multi-site setup. You could use the
    get_current_site().domain, for example, as that is unique across a Django
    project.

    Additionally, all headers from the response's Vary header will be taken
    into account on caching -- just like the middleware does.
    """
    # We also add some asserts to give better error messages in case people are
    # using other ways to call cache_page that no longer work.
    if len(args) != 1 or callable(args[0]):
        raise TypeError("cache_page has a single mandatory positional argument: timeout")
    cache_timeout = args[0]
    cache_alias = kwargs.pop('cache', None)
    key_prefix = kwargs.pop('key_prefix', None)
    if kwargs:
        raise TypeError("cache_page has two optional keyword arguments: cache and key_prefix")

    return decorator_from_middleware_with_args(CacheMiddleware)(
        cache_timeout=cache_timeout, cache_alias=cache_alias, key_prefix=key_prefix
    )

【问题讨论】:

如果我没记错的话,如果你不小心,装饰器有时会“隐藏”异常。我期望发生的是User.objects.get(...) 在你的装饰器中抛出一个异常并且装饰器返回失败的函数,这意味着它是一个函数对象,但该对象缺少它需要的get。将装饰器的内部包裹在 try/except 中并打印出您的异常。可能会让您更准确地描述问题。 好的,我会试一试,但我认为这是因为我返回的 decorator_from_middleware_with_args 不会调用视图。 如果decorator_from_middleware_with_args 没有定义get 函数,那么你绝对正确,它可能来自那里。而且由于您要在装饰器中返回装饰器……是的,这可能是个问题。 【参考方案1】:

你在 else 子句中返回了另一个装饰器。但此时您需要实际调用修饰函数并返回其结果,就像在 if 子句中所做的那样。

(请注意,您发布的原始装饰器只有一个功能级别,并从该级别返回装饰器,而不是像您尝试做的那样从内部返回。)

编辑您需要在此处执行该装饰器的操作:即检查缓存,然后返回缓存的项目或调用视图。

说实话,最简单的事情就是直接编写该功能。去掉了装饰器和中间件为使它们通用化所做的所有逻辑,它只是:

key = get_cache_key(request, key_prefix=key_prefix, 'GET', cache=cache_alias)
response = cache.get(cache_key)
if response is None:
    response = func(*fargs, **fkwargs)
    cache.set(cache_key, response, cache_timeout)
return response

【讨论】:

谢谢,不知道要返回什么。我不能只返回 func 因为我需要执行中间件。试过 return CacheMiddleware(cache_timeout=cache_timeout, cache_alias=cache_alias, key_prefix=key_prefix).process_request(request) or func(*fargs, **fkwargs) 不会引发错误,但它也不会缓存任何东西。

以上是关于Django 自定义装饰器 - 函数没有属性 get的主要内容,如果未能解决你的问题,请参考以下文章

菊花链 Python/Django 自定义装饰器

Django 自定义视图装饰器

用于重定向的 Django 自定义装饰器

Django学习笔记第八篇--实战练习四--为你的视图函数自定义装饰器

芹菜任务和自定义装饰器

Django自定义装饰器