Безопасность темы с тегами шаблона
После прочтения этого документа о безопасности потоков у меня осталось ощущение, что в документации чего-то не хватает, или я это читаю, или мои рассуждения.
Давайте приведем простой пример:
class HelloWorldNode(template.Node):
def render(self, context):
return "O HAI LOL"
@register.tag(name="hello_world")
def hello_world(parser, tokens):
"""
Greets the world with wide-eyed awe.
"""
return HelloWorldNode()
Я понял этот код, чтобы построить новый экземпляр HelloWorldNode
класс всякий раз, когда hello_world
тег используется. Другие примеры включают передачу аргументов в конструктор, например:
class HelloWorldNode(template.Node):
def __init__(self, message):
self.message = message
def render(self, context):
return "O HAI LOL " + message
@register.tag(name="hello_world")
def hello_world(parser, tokens):
"""
Greets the world with wide-eyed awe.
"""
message = tokens.split_contents()[1]
return HelloWorldNode(message)
Таким образом, когда hello_world
выполняется, создается новый экземпляр HelloWorldNode, а словарь экземпляра имеет атрибут message
, Этот экземпляр, безусловно, должен использоваться только для визуализации только данного экземпляра тега, поскольку использование его для других визуализаций будет означать, что привязанные к нему данные будут неверными. Если бы это было не так, аргументы были бы перепутаны между различными использованиями тега.
Глядя на другие примеры из документов, вот упрощенный пример здесь:
def do_current_time(parser, token):
tag_name, format_string = token.split_contents()
return CurrentTimeNode(format_string[1:-1])
Так как это берет данные из токенов, переданных в функцию, единственный способ, которым может работать CurrentTimeNode, - каждый раз создавать новый экземпляр. do_current_time
вызывается.
Вернуться на страницу документации, где возникает диссонанс. Это "плохо".
class CycleNode(Node):
def __init__(self, cyclevars):
self.cycle_iter = itertools.cycle(cyclevars)
def render(self, context):
return self.cycle_iter.next()
В документе говорится, что две страницы, использующие один и тот же тег, могут столкнуться с условиями гонки, если они обе используют один и тот же узел. Я не понимаю, как рендеринг двух шаблонов может привести к совместному использованию одного и того же экземпляра, если они оба независимо создадут свой экземпляр.
Способ решить эту проблему, говорит документация, выглядит следующим образом:
class CycleNode(Node):
def __init__(self, cyclevars):
self.cyclevars = cyclevars
def render(self, context):
if self not in context.render_context:
context.render_context[self] = itertools.cycle(self.cyclevars)
cycle_iter = context.render_context[self]
return cycle_iter.next()
Это похоже на индекс context.render_context
с self
, Смысл этого должен быть self
используется для идентификации экземпляра одним из двух способов:
self
ссылается на один конкретный экземпляр класса во всей системеself
ссылается только на этот класс, и для ссылки на экземпляр требуется контекст рендеринга
Если 1 верно, почему бы просто не связать данные с self
?
Если 2 имеет значение true, а контекст рендеринга "связан с контекстом шаблона, который отображается в данный момент", как можно отличить два экземпляра тега шаблона на одной и той же странице?
Создается ли узел индивидуально каждый раз, когда вызывается тег? Если да, то почему проблемы с параллелизмом? Если нет, то почему нет?
1 ответ
Получил это при более внимательном прочтении этого.
Шаблон компилируется при загрузке. Все аргументы, передаваемые в функцию тега, являются "статическими". Это либо буквенные строки, либо строки, которые используются в качестве идентификаторов для поиска связанных переменных в контексте рендеринга.
Поэтому объект Node создается для каждого тега и остается готовым к использованию всякий раз, когда используется шаблон (и, естественно, шаблон может использоваться в любом количестве потоков).
Таким образом self
в моем вопросе личность конкретного узла в шаблоне. В сочетании с контекстом рендеринга это дает уникальную идентификацию, в которую можно вешать переменные экземпляра.