Django templatetag "порядок обработки"

Я пытаюсь написать набор тегов шаблонов, которые позволят вам легко указать js и css файлы из самих файлов шаблонов. Что-то вдоль строк {% requires global.css %}, а затем в запросе {% get_required_css %}.

У меня это в основном работает, но есть пара вопросов. Мы начнем с вопросов "времени".

Каждый тег шаблона состоит из двух шагов: call/init и render. Каждый вызов /init происходит до вызова процедуры визуализации. Чтобы гарантировать, что все файлы поставлены в очередь до рендеринга {% get_required_css %}, мне нужно собрать список необходимых файлов в самих процедурах call/init.

Итак, мне нужно собрать все файлы в один пакет за запрос. context dict, очевидно, является местом для этого, но, к сожалению, вызов /init не имеет доступа к переменной контекста.

Это смысл? Кто-нибудь видит путь вокруг этого (не прибегая к глобальному объекту request hack-y)?

Другая возможность сохранить их в локальном dict, но их все равно нужно привязать к запросу каким-то образом... возможно, что-то вроде тега {% start_requires %}? Но я не знаю, как это сделать.

Ответ 1

Я придумал способ сделать это, что больше подходит вашим потребностям. Он будет иметь немного больше нагрузки на сервер, но правильное кэширование может помочь облегчить большую часть этого. Ниже я изложил способ, который должен работать, если CSS содержит одинаковые для каждого пути. Вам нужно создать единое представление, чтобы включить все эти файлы, но вы можете оптимизировать свой CSS с помощью этого метода, делая только один вызов CSS для каждой страницы.

import md5
class LoadCss(template.Node):
    def __init__(self, tag_name, css):
        self.css = css
        self.tag_name = tag_name
    def render(self, context):
        request = context['request']
        md5key = md5.new(request.path).hexdigest()
        if md5key not in request.session:
            request.session[md5key] = list()
        ## This assumes that this method is being called in the correct output order.
        request.session[md5key].append(self.css)
        return '<!-- Require %s -->' % self.css
def do_load_css(parser, token):
    tag_name, css = token.split_contents()
    return LoadCss(tag_name, key)
register.tag('requires', do_load_css)

class IncludeCss(template.Node):
    def __init__(self, tag_name):
        self.tag_name = tag_name
    def render(self, context):
        request = context['request']
        md5key = md5.new(request.path).hexdigest()
        return '<link rel="stylesheet" href="/path/to/css/view/%s">' % md5key
def do_include_css(parser, token):
    return IncludeCss(token)
register.tag('get_required_css', do_include_css)

views.py:

from django.conf import settings
from django.views.decorators.cache import cache_page
import os

@cache_page(60 * 15) ## 15 Minute cache.
def css_view(request, md5key):
    css_requires = request.session.get(md5key, list())
    output = list()
    for css in css_requires:
        fname = os.path.join(settings.MEDIA_ROOT, 'css', css) ## Assumes MEDIA_ROOT/css/ is where the CSS files are.
        f = open(fname, 'r')
        output.append(f.read())
    HttpResponse(''.join(output), mimetype="text/css")

Это позволяет хранить информацию о CSS в контексте, затем в сеансе и выполнять вывод из представления (с кешированием, чтобы сделать его быстрее). Разумеется, это будет немного больше накладных расходов на сервер.

Если вам нужно изменить CSS больше, чем просто путь, вы можете просто изменить строки md5 в соответствии с вашими потребностями. У вас есть доступ ко всему объекту запроса и контексту, поэтому почти все должно быть там.

Остерегайтесь: Во втором обзоре это может привести к состоянию гонки, если браузер извлекает CSS до того, как сессия будет заполнена. Я не верю, что Django работает именно так, но сейчас мне не хочется искать его.