模板标签的线程安全性

9 投票
1 回答
638 浏览
提问于 2025-04-17 02:40

在阅读了关于线程安全的这篇文档后,我感觉文档中似乎缺少了一些东西,或者是我理解得不够透彻,或者是我的推理有问题。

我们来举个简单的例子:

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()

我理解这段代码是每次使用hello_world标签时,都会构造一个新的HelloWorldNode类的实例。其他的例子则涉及到向构造函数传递参数,比如这样:

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()

这似乎是用self来索引context.render_context。这意味着self可以通过两种方式来识别实例:

  1. self引用系统中某个特定的类实例
  2. self仅引用该类,要引用实例则需要一个渲染上下文

如果第1种情况成立,为什么不直接把数据和self关联起来呢?

如果第2种情况成立,而渲染上下文是“与当前正在渲染的模板的上下文相关联的”,那么如何区分同一页面上两个模板标签的实例呢?

每次调用标签时,Node是单独实例化的吗?如果是这样,为什么会有并发问题?如果不是,为什么不这样做呢?

1 个回答

1

通过仔细阅读这个链接,我明白了。

模板在加载时就会被编译。传递给标签函数的任何参数都是“静态”的。它们要么是直接的字符串,要么是用作标识符的字符串,用来查找渲染上下文中的绑定变量。

所以,每个标签都会创建一个Node对象,并且这个对象会一直存在,随时可以在模板使用时调用(而且模板可以在多个线程中使用)。

因此,我问题中的self就是模板中特定Node的身份。结合渲染上下文,这样就能为实例变量提供一个独特的身份。

撰写回答