Jinja2 教程 包含和导入
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jinja2 教程 包含和导入相关的知识,希望对你有一定的参考价值。
参考技术A欢迎来到我的 Jinja2 教程系列的另一部分。到目前为止,我们已经了解了很多关于渲染、控制结构和各种功能的知识。在这里,我们将开始讨论帮助我们处理组织模板的语言特性。我们将看的第一个构造是 include 和 import 语句。
Include 和 Import 语句是 Jinja 为我们提供帮助组织模板集合的一些工具,尤其是当这些模板的大小增加时。
通过使用这些结构,我们可以将模板拆分为更小的逻辑单元,从而生成具有明确定义范围的文件。反过来,当出现新需求时,这将使修改模板变得更加容易。
结构良好的模板集合的最终目标是提高可重用性和可维护性。
“包含”语句允许您将大型模板分解为更小的逻辑单元,然后可以在最终模板中组装这些单元。
当你使用时, include 你引用另一个模板并告诉 Jinja 渲染引用的模板。Jinja 然后将渲染文本插入到当前模板中。
语法为 include :
% include \'path_to_template_file\' %
其中 \'path_to_template_file\' 是我们想要包含的模板的完整路径。
例如,下面我们有一个名为模板的模板, cfg_draft.j2 它告诉 Jinja 找到名为模板的模板,渲染它,并用渲染的文本 users.j2 替换块。 % include ... %
cfg_draft.j2
users.j2
最后结果:
如果您查看典型的设备配置,您将看到与给定功能相对应的许多部分。您可能有接口配置部分、路由协议一、访问列表、路由策略等。我们可以编写单个模板来生成整个配置:
device_config.j2
通常我们会有机地扩展我们的模板,并将一个接一个的部分添加到负责生成配置的单个模板中。然而,随着时间的推移,这个模板变得太大,并且变得难以维护。
处理日益增长的复杂性的一种方法是识别大致对应于单个特征的部分。然后我们可以将它们移动到自己的模板中,这些模板将包含在最后一个模板中。
我们的目标是使用一些较小的模板来处理明确定义的功能配置部分。这样,当您需要进行更改时,更容易找到要修改的文件。也更容易分辨哪个模板做什么,因为我们现在可以给它们适当的名称,如“bgp.j2”和“acls.j2”,而不是一个名为“device_config.j2”的大模板。
使用前面的模板,我们可以将其分解为更小的逻辑单元:
base.j2
dns.j2
ntp.j2
interfaces.j2
prefix_lists.j2
bgp.j2
我们现在有一组单独的模板,每个模板都有一个明确的名称来传达其用途。虽然我们的示例没有太多行,但我认为您会同意我的观点,即我们得出的逻辑分组将更易于使用,并且可以更快地建立关于这里发生的事情的心理模型。
随着功能移动到单独的模板,我们终于可以使用 include 语句来组成我们的最终配置模板:
config_final.j2
您打开此模板,只需快速浏览一下,您就应该能够了解它正在尝试做什么。它更简洁,我们可以轻松添加不会在数百行其他行中丢失的注释。
作为奖励,您可以通过注释单行或简单地暂时删除它来快速测试禁用一个功能的模板。
现在更容易进行更改,并且可以更快地识别特征和相应的模板,而不是搜索一个可能有数百行的大模板。
同样,当需要新部分时,我们可以创建单独的模板并将其包含在最终模板中,从而实现我们增加模块化的目标。
base.j2
您可以 include 在模板层次结构中的任何级别以及您想要的模板中的任何位置使用语句。这正是我们在这里所做的;我们将横幅的文本移动到一个单独的文件中,然后我们将其包含在 base.j2 模板中。
我们可以争辩说,横幅本身并不重要,不足以保证有自己的模板。但是,还有另一类 include 有用的用例。我们可以维护跨许多不同模板使用的通用片段库。
这与我们之前的示例不同,在之前的示例中,我们将一个大模板分解为更小的逻辑单元,所有这些单元都与最终模板紧密相关。使用通用库,我们可以在许多不同的模板中重复使用这些单元,这些模板可能没有任何相似之处。
Jinja 允许我们 optionally 通过 ignore missing 向 include .
% include \'guest_users.j2\' ignore missing %
它本质上告诉 Jinja 寻找 guest_users.j2 模板并在找到时插入呈现的文本。如果找不到模板,这将导致空行,但不会引发错误。
我通常建议不要在您的模板中使用它。它不是被广泛使用的东西,所以阅读您的模板的人可能不知道它的用途。最终结果还取决于特定文件的存在,这可能会使故障排除更加困难。
有更好的方法来处理可选特性,其中一些依赖于我们将在下一篇文章中讨论的模板继承。
与“忽略缺失”密切相关的是提供要包含的模板列表的可能性。Jinja 将检查模板是否存在,包括第一个存在的模板。
在下面的示例中,如果 local_users.j2 不存在但 radius_users.j2 存在,则渲染 radius_users.j2 最终将被插入。
% include [\'local_users.j2\', \'radius_users.j2\'] %
您甚至可以将模板列表与 ignore missing 参数结合起来:
% include [\'local_users.j2\', \'radius_users.j2\'] ignore missing %
这将导致搜索列出的模板, 如果没有找到它们,则不会引发错误 。
同样,虽然很诱人,但我建议不要使用此功能,除非您用尽其他途径。如果在我的最终渲染中有些东西看起来不正确,我不会喜欢弄清楚列出的模板中的哪一个最终被包含在内。
总而言之,您可以使用“包含”来:
在 Jinja 中,我们使用 import 语句来访问保存在其他模板中的宏。这个想法是经常在他们自己的文件中使用宏,然后将这些宏导入需要它们的模板中。
我们可以通过三种方式导入宏。
所有三种方式都将使用导入以下模板:
macros/ip_funcs.j2
您还可以将 2 与 3 结合起来:
imp_ipfn_way2_3
我的建议是始终使用 1 . 这迫使您通过显式命名空间访问宏。方法 2 和 3 风险与当前命名空间中定义的变量和宏发生冲突。正如 Jinja 中经常出现的情况一样,显式优于隐式。
导入被缓存,这意味着它们在每次后续使用时都会非常快速地加载。这是要付出代价的,即导入的模板无法访问导入它们的模板中的变量。
这意味着默认情况下,您无法访问从另一个文件导入的宏内部的上下文中传递的变量。
相反,您必须构建宏,以便它们仅依赖于显式传递给它们的值。
为了说明这一点,我编写了两个版本的宏命名 def_if_desc ,一个尝试访问可用于模板导入的变量。另一个宏依赖于通过值显式传递给它的字典。
两个版本都使用以下数据:
default_desc.yaml
希望现在您可以看到导入宏时的默认行为。由于上下文中的变量值可以随时更改,Jinja 引擎无法缓存它们,我们也不允许从宏中访问它们。
但是,如果出于某种原因您认为允许宏访问上下文变量是个好主意,您可以使用 with context 传递给 import 语句的附加参数来更改默认行为。
Note: This will automatically disable caching.
为了完整起见,这是我们如何“修复”失败的宏:
macros/def_desc_ctxvars.j2
im_defdesc_vars_wctx.j2
现在它起作用了:
就个人而言,我不认为 import 一起使用是一个好主意 with context 。从单独的文件中导入宏的全部意义在于允许它们在其他模板中使用并利用缓存。可能有数百个,如果不是数千个,并且一旦您使用 with context 缓存就消失了。
我还可以在依赖于从模板上下文访问变量的宏中看到一些非常微妙的错误。
为了安全起见,我会说坚持标准导入并始终使用命名空间,例如
% import \'macros/ip_funcs.j2\' as ipfn %
我们了解了两种 Jinja 构造,它们可以帮助我们管理随着模板大小增加而出现的复杂性。通过利用 import 和 include 语句,我们可以 提高可重用性并使我们的模板更易于维护 。我希望包含的示例向您展示了如何使用这些知识来使您的模板集合更好地组织和更容易理解。
这是我在以下情况下使用的内容的快速摘要:
以上是关于Jinja2 教程 包含和导入的主要内容,如果未能解决你的问题,请参考以下文章