简体   繁体   中英

Django custom decorator - function has no attribute get

I'm trying to create my own decorator based on @cache_page decorator. My decorator should work exactly like @cache_page except when slug attribute of the view matches request.user.userprofile slug, then normally process the view and return not cached response

Pseudocode:

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

My decorator:

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

This works if user matches the slug. Otherwise, it raises:

Exception Value:
'function' object has no attribute 'get'

Full traceback:

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'

Do you know where is the problem? The best would be if there were caching for user and other users separately. So 2 caches for this view.

EDIT: This is an original @cache_page decorator

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
    )

You're returning yet another decorator in the else clause. But at that point you need to be actually calling the decorated function and returning its result, as you do in the if clause.

(Note, the original decorator you post only has a single level of function and returns the decorator from that level, not from inside as you are trying to do.)

Edit You need to do the action of that decorator here: that is, check the cache, and either return the cached item or call the view.

To be honest, the easiest thing is to simply write that functionality directly. Stripped of all the logic the decorator and middleware do to make them generic, it's just:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM