如何确保 Jinja 自定义标签只输出一次?
我在 Jinja2 中有一个自定义标签,我只想在第一次调用它时输出一些内容。假设我有以下模板:
1. {% only_once %}
2. {% only_once %}
3. {% only_once %}
我希望输出为:
1. "I only get printed once!"
2.
3.
我猜最好的方法是在模板的上下文中设置一个标志来跟踪我是否已经打印了某些内容。这是一个代码示例,但这对吗?
class OnlyOnceExtension(Extension):
tags = set(['only_once'])
@contextfunction
def parse(self, context, parser):
if hasattr(context, 'my_flag') and context.my_flag:
return Output("")
else:
return Output("I only get printed once!")
这是正确的吗?我读到一些关于上下文是不可变的东西,所以这不起作用吗? (参见 http://jinja.pocoo.org/2/documentation/api 和搜索不可变)
I have a custom tag in Jinja2 that I want to output something only the first time that it is called. So say I have the following template:
1. {% only_once %}
2. {% only_once %}
3. {% only_once %}
I want the output to be:
1. "I only get printed once!"
2.
3.
I'm guessing the best way to do this is to set a flag in the context of the template to track whether I've already printed something or not. Here's a code sample, but is this right?
class OnlyOnceExtension(Extension):
tags = set(['only_once'])
@contextfunction
def parse(self, context, parser):
if hasattr(context, 'my_flag') and context.my_flag:
return Output("")
else:
return Output("I only get printed once!")
Is that correct? I read some stuff about the context is immutable, so will this not work? (see http://jinja.pocoo.org/2/documentation/api and search for immutable)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果你想纯粹用 Jinja 来做,你可以以这种方式检查loop.index变量,
If you want to do it purely with Jinja you can just check the loop.index variable in that fashion,
我的建议是在 Python 代码中实现这一点:
在 Python 代码中创建一个
OnlyOnce
实例并将其传递给模板,然后每次要使用它时,只需使用{{ only_once }}
。我注意到很多使用 Jinja 的人都想以 Django 式的方式做事,即编写扩展。但是 Jinja 的表达式/导入/任何东西都足够强大,您不必为所有事情使用扩展。
是的,使用 context.my_flag 是个坏主意。只有模板可以修改上下文。 曾经。
My suggestion is to implement this in Python code:
Create an
OnlyOnce
instance in your Python code and pass it to the template, and then every time you want to use it, just use{{ only_once }}
.One thing I notice about a lot of people using Jinja is that they want to do things in a Django-ish way, that is, writing extensions. But Jinja's expressions/importing/whatever is powerful enough that you don't have to use extensions for everything.
And yes, using the
context.my_flag
thing is a bad idea. Only the template gets to modify the context. EVER.我会定义一个布尔值和一个宏,然后从那里开始。您可以打印宏的结果,而不是打印变量,该宏使用 if 语句和布尔值来决定是否应该打印。所以,你会得到以下结果:
I would define a boolean and a macro and go from there. Instead of printing the variable, you print the result of the macro which uses an if statement and the boolean to decide if it should print. So, you get the following:
Jinja 并不总是重新评估宏,因此设置宏变量不会阻止值被创建两次。可接受的解决方案有效,但它要求您在烧瓶视图级别对对象进行硬编码。
我通过创建一个自定义 jinja 过滤器解决了这个问题,该过滤器每个请求只返回一次值。
过滤器:
将过滤器添加到您的 Flask 应用
创建一个包含以下内容的 jinja 宏:您只想输出一次的文本:
Macros.html
并使用它:
Jinja doesn't always re-evaluate macros, so setting a macro variable won't prevent a value from being created twice. The accepted solution works, but it requires you to hard code your object at the flask view level.
I solved this by creating a custom jinja filter that will only return the value once per request.
The filter:
Add the filter to your flask app
Create a jinja macro containing the text your only want to output once:
macros.html
And to use it:
以前的答案实际上在它们自己的上下文中相当不错,但我最近不得不解决这个问题,并且我不得不稍微偏离,这导致了我认为更“通用”的解决方案。
在我看来,使用“循环变量”是执行此操作的正确/优雅的方法,但这仅在您开始使用循环时才有效。
就我而言,我有一个递归宏(我有需要遍历的复杂嵌套结构),因此仅在宏第一次实际渲染某些内容时渲染“foo”并不是非常简单。
我尝试使用 解决方案 的变体来完成此任务 近乎整体,使用 命名空间,尽管它有效,但结果非常丑陋,因为我必须多次重复同一个块:
我最终所做的类似于 user2682863 在他们的解决方案中建议。
定义并实例化一个
Once
对象,其中将存储属性以供将来参考;然后,在过滤器本身中,我们检查该属性是否已存储,以决定是否应该渲染它。这稍微好一些,因为值本身用作要存储的属性(其名称空间哈希),因此使用过滤器更干净:
定义宏(您可以使用输入):
使用它:
Previous answers are actually pretty good in their own context, but I recently had to tackle this, and I had to deviate slightly, which led into what I think is a more "generic" solution.
Using "loop variables" is IMO the right/elegant way to do this, but this only works if you are using loops to begin with.
In my case, I have a recursive macro (I have complex nested structures I need to traverse), so rendering "foo" only the first time the macro actually renders something was not super straightforward.
I tried to accomplish this with a variation of the solution suggested by nearlymonolith, using namespaces, and even though it worked, the result was really ugly, as I had to duplicate the same block multiple times:
What I ended up doing was something similar to what user2682863 suggested in their solution.
Define and instantiate a
Once
object where attributes will be stored for future reference; then, in the filter itself we check if the attribute has already been stored or not to decide whether it should be rendered.This is slightly better because the value itself is used as the attribute to store (its name-spaced hash), so using the filter is cleaner:
Define the macro (you can use inputs):
Use it: