如何递归获取Django模板中所有{% extend/include %}加载的模板路径?
Django递归获取模板依赖的解决方案
我使用Django框架,有一个名为name.html的模板,内容如下:
{% extends "some.html" %} {% for page in pages %} {% include page.html_template_path %} {% endfor %}
该模板会引入存储在page.html_template_path中的模板路径,且每个模板内部还可能继承或引入其他模板。现在需要递归获取所有通过{% extend ... %}和{% include %}标签加载的模板路径,但存在以下限制:
- 正则表达式不可行(模板路径可能存在于变量、上下文或
{% for %}标签等场景中) - 替换内置
{% extend %}和{% include %}标签为自定义标签的方案不适用于我的项目 - 尝试过的django-template-analyzer工具无法正确处理嵌套
{% for %}这类复杂场景
基于Django模板AST解析的实现思路
Django模板系统会将模板解析为抽象语法树(AST),我们可以通过遍历AST节点识别extend和include标签,再递归处理依赖模板,同时覆盖变量、循环等复杂场景:
1. 核心处理逻辑
- 使用Django的
Template类解析模板,获取其节点列表 - 遍历节点,识别
ExtendsNode(对应{% extend %})和IncludeNode(对应{% include %}) - 对
ExtendsNode:直接提取父模板路径,递归解析父模板 - 对
IncludeNode:区分静态路径和动态路径处理:- 静态路径:直接提取字符串路径,递归解析
- 动态路径(如
page.html_template_path这类变量):若能获取上下文数据,代入变量值得到实际路径;若无法获取上下文,可记录变量表达式,或根据项目模板存储规则枚举可能路径
- 处理循环节点(如
ForNode):遍历循环体内的节点,同样识别并处理include标签
2. 示例代码实现
from django.template import Engine, Template from django.template.base import ExtendsNode, IncludeNode, ForNode def get_template_dependencies(template_path, engine=None, visited=None): # 用visited集合避免循环依赖和重复处理 if visited is None: visited = set() if template_path in visited: return visited visited.add(template_path) engine = engine or Engine.get_default() try: # 加载模板源文件 template_source, origin = engine.find_template(template_path) template = Template(template_source) except Exception: # 处理模板不存在/加载失败的情况 return visited # 遍历模板所有节点 for node in template.nodelist: process_node(node, engine, visited) return visited def process_node(node, engine, visited): # 处理extend标签 if isinstance(node, ExtendsNode): parent_path = node.parent_name.var if hasattr(node.parent_name, 'var') else node.parent_name # 静态路径直接递归解析 if isinstance(parent_path, str): get_template_dependencies(parent_path, engine, visited) # 处理include标签 elif isinstance(node, IncludeNode): include_path = node.template_name.var if hasattr(node.template_name, 'var') else node.template_name if isinstance(include_path, str): get_template_dependencies(include_path, engine, visited) else: # 动态路径处理:若能获取上下文,可在此处传入上下文解析变量 # 示例:resolved_path = include_path.resolve(your_context) # get_template_dependencies(resolved_path, engine, visited) pass # 处理for循环节点,遍历循环体内的子节点 elif isinstance(node, ForNode): for child_node in node.nodelist_loop: process_node(child_node, engine, visited) # 处理包含子节点的其他类型节点 elif hasattr(node, 'nodelist'): for child in node.nodelist: process_node(child, engine, visited)
3. 关键注意事项
- 动态路径处理:如果模板路径是变量,必须依赖上下文数据才能解析实际路径;若无法获取上下文,可根据项目模板的命名规则、存储目录枚举可能路径,或仅记录变量表达式
- 异常处理:需捕获模板加载失败的异常,避免整个流程中断
- 循环依赖防护:通过
visited集合记录已处理的模板,防止无限递归
内容的提问来源于stack exchange,提问作者user3905355
相关产品推荐
相关产品推荐

