如何在Jinja2中合并两个字典?替代Ansible combine方案
Jinja2实现YAML字典合并(替代Ansible combine过滤器)
问题根源
你用vars.default.update(vars.new)得到None是因为Python的dict.update()方法是原地修改原字典,它的返回值本来就是None——这是Python字典方法的固有特性,和Jinja2无关,所以直接在模板里调用会输出None,而非合并后的字典。
两种可行解决方案
1. 自定义combine过滤器(推荐,支持递归合并)
Jinja2允许自定义过滤器,我们可以实现一个和Ansible combine逻辑一致的函数,注册到Jinja2环境中:
步骤1:编写Python代码注册过滤器
from jinja2 import Environment, FileSystemLoader import yaml def combine_dicts(base_dict, update_dict, recursive=False): # 先做浅拷贝,避免修改原字典 result = base_dict.copy() result.update(update_dict) # 如果需要递归合并嵌套字典 if recursive: for key, value in update_dict.items(): if isinstance(value, dict) and key in result and isinstance(result[key], dict): result[key] = combine_dicts(result[key], value, recursive=True) return result # 初始化Jinja2环境 env = Environment(loader=FileSystemLoader('.')) # 注册自定义过滤器 env.filters['combine'] = combine_dicts # 加载YAML变量文件 with open('vars.yml', 'r', encoding='utf-8') as f: vars_data = yaml.safe_load(f) # 渲染模板 template = env.get_template('template.yml') output_content = template.render(vars=vars_data) # 保存结果到output.yml with open('output.yml', 'w', encoding='utf-8') as f: f.write(output_content)
步骤2:在template.yml中使用过滤器
# 浅合并(新字典覆盖旧字典同键值) merged_dict: {{ vars.default | combine(vars.new) }} # 如果需要递归合并嵌套字典,加上recursive参数 # merged_dict: {{ vars.default | combine(vars.new, recursive=True) }}
2. 直接用Python字典解包(仅浅合并,无需额外代码)
如果你的Python版本是3.5+,可以直接在Jinja2模板里用{**d1, **d2}的语法实现浅合并,不需要自定义过滤器:
merged_dict: {{ {**vars.default, **vars.new} }}
注意:这种方式只支持浅合并——如果字典里嵌套了子字典,新字典的子字典会直接覆盖旧字典的对应子字典,不会递归合并内层内容。
预期输出(output.yml)
不管用哪种方式,浅合并的结果都会是:
merged_dict: key1: value1 key2: new_value2 key3: value3
内容的提问来源于stack exchange,提问作者craigthackerx
相关产品推荐
相关产品推荐

