14.10.2007 14:24

  1. crash

    0 ↑
    0 ↓
    Хочу вот закешировать несколько тегов с помощью декоратора, тег, например, такой:
    @register.inclusion_tag('tags/user_rating_tag.html',takes_context=True)
    def user_rating(context):
    from django.contrib.auth.models import User
    users = User.objects.extra(select={'title':"news.title",'cnt':'COUNT(*)'},
    tables =['news'],
    where=['news.user_id=auth_user.id GROUP BY auth_user.username']
    ).order_by('-cnt')
    return {
    'users':users,
    'MEDIA_URL':context['MEDIA_URL']
    }
    Когда добавляю к нему @cache_page получаю ошибку
    TemplateSyntaxError at /
    Any tag function decorated with takes_context=True must have a first argument of 'context'
    Кстати, я может неправильно понял кеширование вообще - если я ставлю кеш на вьюшку которая рендерит страницу с указанными тегами, то может мне их кешировать не надо?
  2. cache_page кеширует страницу, а не тег, согласись...что-то в названии декоратора об этом говорит....
    кешировать отдельные теги, как мне кажется, не нужно.
  3. О! Это мой любимый вопрос в последнее время :-)

    К сожалению, с декорированием inclusion_ и simple_tag в Джанго проблемы, потому что юзерский декоратор практически всегда замешивает аргументы декорируемой функции в "*args, **kwargs" (что естественно), а джанговские теговые декораторы хотят видеть первоначальные имена параметров.

    Есть и еще одна проблема, на первый взгляд мелкая, но вот в нашем проекте вставшая неожиданно остро. Мы сначала вытащили все "мясо" тега в отдельную функцию и кешировали ее (некрасиво, но работает):
    @cached
    def _some_tag(context):
    return {...}

    @register.inclusion_tag('template.html', takes_context=True)
    def some_tag(context):
    return _some_tag(context)
    Но этого оказалось недостаточно, потому что таким образом кешируется только создание данных для включаемого шаблона, но не рендеринг их в HTML. Причем у нас в проекте на индексной странице, загруженной такими включаемыми информерами, если HTML закешировать, скорость растет раза в полтора. Поэтому я в итоге написал свой аналог inclusion_tag'а. Если интересно, могу сюда его скинуть в понедельник.
  4. crash

    0 ↑
    0 ↓
    Скинь пожалуйста, потому что ситуация (информеры) абсолютно такая же :)
  5. Код такой:
    def cached(key_func, timeout=settings.CACHE_TIMEOUT):
    '''
    Кеширующий декоратор.
    '''
    def decorator(func):
    def wrapper(*args, **kwargs):
    key = str(key_func(*args, **kwargs))
    value = cache.get(key)
    if value is None:
    value = func(*args, **kwargs)
    cache.set(key, value, timeout)
    return value
    wrapper.__name__ = func.__name__
    return wrapper
    return decorator

    class InformerNode(template.Node):
    '''
    Информер -- inclusion-тег для шаблона, который все данные для себя принимает
    из контекста и умеет кешироваться.

    func: функция, расчитывающая внутренний контекст
    template_name: имя включаемого шаблона
    cached_key_func: функция, примающая контекст и формирующая ключ
    '''
    def __init__(self, func, template_name, cache_key_func):
    self.func = func
    if cache_key_func:
    self.render = cached(cache_key_func)(self.render)
    self.template_name = template_name

    def render(self, context):
    t = loader.get_template(self.template_name)
    return t.render(Context(self.func(context)))

    def informer(template_name, cache_key_func=None):
    '''
    Декоратор, делающий из функции подготовки контекста для информера, функцию,
    пригодную для регистрации в качестве тега
    '''
    def decorator(func):
    def wrapper(parser, token):
    return InformerNode(func, template_name, cache_key_func)
    wrapper._decorated_func = func
    wrapper.__name__ = func.__name__
    return wrapper
    return decorator
    Используется примерно так:
    @register.tag
    @informer('template.html', lambda c: ....)
    def my_tag(context):
    return {
    'value': value,
    }
    Единственная необычность — это lambda, принимающая контекст в параметре информера. Она должна вернуть строчку ключа кеша, в котором информер хранить. В прстейшем случае, если информер одинаков на всех страницах, это будет просто константа типа "lambda c: 'myinformer'". Если зависит от имени юзера, напрмер, то "lambda c: ;myinformer-' + c['user'].username". Ну и в таком духе.
  6. crash

    0 ↑
    0 ↓
    Спасибо большое, буду применять!
  7. crash

    0 ↑
    0 ↓
    А вот еще вопрос, как работать с кешем, когда большая часть страницы, которая кешируется, изменилась, можно ли как-то сказать джанге очистить и сгенерить его заново?
  8. crash

    0 ↑
    0 ↓
    Хм, у меня с этим тегом скорость еще меньше чем без кеширования, странно очень. Использую пока дисковый кеш.
  9. А чего не memcached? У меня прирост был как раз из-за того, что я сэкономил на всяких loader.get_template, которые файлы с диска читают.
  10. crash

    0 ↑
    0 ↓
    Он на подходе, никак саппорт не поставит (сам я могу, но такая ситуация). Хотя если он будет еще шустрее чем дисковый, то вообще сказка :))
    Пока же остановился на схеме CacheMiddleware для анонимов, и внутри на твоем теге сделаны редко изменяющиеся блоки. На анониме выходит 200 запросов в секунду, на залогиненом (там большая часть страницы меняется активно, поэтому кешировать все не выходит) - 25-50. В принципе я доволен :)
  11. О, да, это совсем неплохо :-)
  12. dogada

    0 ↑
    0 ↓
    на анониме должна быть 1000 запросов/сек с сервером 1GHz/1Gb. тогда можно работать.
  13. crash

    0 ↑
    0 ↓
    Даже у twitter 600, а у меня загрузка 30-40К просмотров в день :)
  14. crash

    0 ↑
    0 ↓
    Кстати в воскресенье в джангу добавили кеширующий тег, для части шаблона, очень полезная штука.
    http://www.djangoproject.com/documentation/cache/#template-fragment-caching
  15. Да, приятная штука! Сойдет там, где кеш только по времени протухает.
  16. romankrv

    0 ↑
    0 ↓
    а что изменилось в джанге со времени написания данного поста — в плане Кеширование inclusion tags

    ЗЫ
    а данный форум не шлет уведомления на почту о новых комментариях?

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.