在Superset中使用Jinja 模板的使用


今天推荐一个 Preset.io 的 现场演示,Jinja templating in Superset[1]
Jinja 模板使最终用户能够增强他们的 SQL 查询并构建更多动态仪表板。在这个现场演示中,我们将展示 Jinja 模板语法的工作原理,教授一些常见的工作流程,并分享一些使用此功能集构建高质量分析体验的技巧。
顺便说下,利用昨天介绍的从视频中提取 PPT,并转为 PDF, 制作了 PDF,有兴趣的,请留言关键词 001 获取。

PPT
Jinja
Jinja2[2] 是一个 Python 的功能齐全的模板引擎。它有完整的 unicode 支持,一个可选的集成沙箱执行环境,被广泛使用,以 BSD 许可证授权。
Jinja 的优点,
安全: 强大的 HTML 自动转义系统保护系统免受 XSS 攻击。
编译快速: 及时编译最优的 python 代码。
易于调试。异常的行数直接指向模板中的对应行。
具有模板继承的特性,减少大量的工作量。
一个简单的例子,
<title>{% block title %}{% endblock %}</title><ul>{% for user in users %}  <li><a href="{{ user.url }}">{{ user.username }}</a></li>{% endfor %}</ul>
Superset 与 Jinja 模板
SQL Lab 和 Explore(探索) 支持查询中的 Jinja 模板[3]。要启用模板,需要在  superset_config.py 中设置 ENABLE_TEMPLATE_PROCESSING ,
# activate Jinja templatingFEATURE_FLAGS = {  "ENABLE_TEMPLATE_PROCESSING": True}
当启用模板时,python 代码可以嵌入到虚拟数据集(virtual datasets), Explore 中的 filter(过滤器)和 metrics(聚合控件), 自定义 SQL。默认情况下,以下变量在 Jinja 上下文中可用:
columns: 在查询中分组的列
filter:在查询中应用的过滤器
from_dttm:datetime 所选时间范围的起始值(None 如果未定义)
to_dtt:datetime, 所选时间范围的结束值(None 如果未定义)
groupby:在查询中分组的列(不推荐)
metrics: 查询中的聚合表达式
row_limit: 查询的行数限制
row_offset: 查询的行偏移量
table_columns:数据集中可用的列
time_column:查询的时间列(None 如果未定义)
time_grain:选定的时间粒度(None 如果未定义)
例如,要将时间范围添加到虚拟数据集,您可以编写以下内容:
SELECT  *  from tbl where dttm_col >  ' {{ from_dttm }} '  and dttm_col <  ' {{ to_dttm }} '
要向 Jinja 上下文添加自定义功能,您需要通过 JINJA_CONTEXT_ADDONS 在  superset_config.py 中定义来重载环境中的默认 Jinja 上下文。本词典中引用的对象可供用户在 Jinja 上下文可用的地方使用。
JINJA_CONTEXT_ADDONS  = {     'my_crazy_macro' : lambda  x : x * 2 ,}
CUSTOM_TEMPLATE_PROCESSORS 除了默认的 Jinja 模板,SQL Lab 还通过在 superset_config.py 中设置来支持自定义模板处理器。此字典中的值会覆盖指定数据库引擎的默认 Jinja 模板处理器。
SQL Lab 还包括带有可插拔后端的实时查询验证功能。您可以通过在配置文件中添加如下块来配置哪个验证实现与哪个数据库引擎一起使用:
FEATURE_FLAGS  = {     'SQL_VALIDATORS_BY_ENGINE' : {         'presto' : 'PrestoDBSQLValidator' ,    }}
可用的验证器和名称可以在  sql_validators[4] 中找到。
可用的宏
在本节中,我们将介绍 Superset 中预定义的 Jinja 宏。
当前用户名
{{ current_username() }} 宏返回当前登录用户的用户名。
如果您在 Superset 配置中启用了缓存,则默认情况下 Superset 在计算缓存键时将使用 username。缓存键是一个唯一标识符,它确定将来是否有缓存命中,并且 Superset 可以检索缓存的数据。
username 您可以通过在 Jinja 代码中添加以下参数来禁用在缓存键的计算中包含该值:
{{ current_username(add_to_cache_keys=False) }}
当前用户 ID
该 {{ current_user_id() }} 宏返回当前登录用户的 user_id。
如果您在 Superset 配置中启用了缓存,则默认情况下 Superset 在计算缓存键时将使用 user_id。缓存键是一个唯一标识符,它确定将来是否有缓存命中,并且 Superset 可以检索缓存的数据。
user_id 您可以通过在 Jinja 代码中添加以下参数来禁用在缓存键的计算中包含该值:
{{ current_user_id(add_to_cache_keys=False) }}
自定义 URL 参数
该 {{ url_param('custom_variable') }} 宏允许您定义任意 URL 参数并在 SQL 代码中引用它们。
这是一个具体的例子:
您在 SQL Lab 中编写以下查询:
SELECT count(*)FROM ORDERSWHERE country_code = '{{ url_param('countrycode') }}'
您在域 www.example.com[5] 上托管 Superset,并向您在西班牙的同事发送以下 SQL Lab URL www.example.com/superset/sqllab?countrycode=ES ,向您在美国的同事发送以下 SQL Lab URL www.example.com/superset/sqllab?countrycode=US
对于您在西班牙的同事,SQL Lab 查询将呈现为:
SELECT count(*)FROM ORDERSWHERE country_code = 'ES'
对于您在美国的同事,SQL Lab 查询将呈现为:
SELECT count(*)FROM ORDERSWHERE country_code = 'US'
在缓存键中显式包含值
该 {{ cache_key_wrapper() }} 函数显式指示 Superset 将一个值添加到用于计算缓存键的累积值列表中。
仅当您要将自己的自定义函数返回值包装在缓存键中时才需要此函数。您可以在此处[6]获得更多上下文  。
请注意,此函数支持 and 函数调用中的 user_id 和 username 值的缓存(如果您启用了缓存)。current_user_id(), current_username()
过滤值
您可以使用 将特定过滤器的值作为列表检索 {{ filter_values() }}。
这在以下情况下很有用:
您想使用过滤器组件来过滤过滤器组件列的名称与 select 语句中的名称不匹配的查询
您希望能够在主查询中进行过滤以提高性能
这是一个具体的例子:
SELECT action, count(*) as timesFROM logsWHERE    action in ({{ "'" + "','".join(filter_values('action_type')) + "'" }})GROUP BY action
特定列的过滤器
该 {{ get_filters() }} 宏返回应用于给定列的过滤器。除了返回值(类似于  filter_values())之外,get_filters() 宏还返回在探索 UI 中指定的运算符。
这在以下情况下很有用:
您想要处理的不仅仅是 SQL 子句中的 IN 运算符
您想要为过滤器生成自定义 SQL 条件
您希望能够在主查询中进行过滤以提高速度
这是一个具体的例子:
 WITH RECURSIVE    superiors(employee_id, manager_id, full_name, level, lineage) AS (    SELECT        employee_id,        manager_id,        full_name,    1 as level,    employee_id as lineage    FROM        employees    WHERE    1=1    {# Render a blank line #}    {%- for filter in get_filters('full_name', remove_filter=True) -%}    {%- if filter.get('op') == 'IN' -%}        AND        full_name IN ( {{ "'" + "', '".join(filter.get('val')) + "'" }} )    {%- endif -%}    {%- if filter.get('op') == 'LIKE' -%}        AND        full_name LIKE {{ "'" + filter.get('val') + "'" }}    {%- endif -%}    {%- endfor -%}    UNION ALL        SELECT            e.employee_id,            e.manager_id,            e.full_name,    s.level + 1 as level,    s.lineage        FROM            employees e,        superiors s        WHERE s.manager_id = e.employee_id    )    SELECT    employee_id, manager_id, full_name, level, lineage    FROM    superiors    order by lineage, level
想获取更多关于 Supersert 与 jinja 模板 的信息,可以移步我放在 B 站的视频,https://www.bilibili.com/video/BV1Jq4y1i7Ny/, 也可以回复 001 获取 PPT(PDF格式)。
参考
Jinja Templating in Superset[7]
SQL Templating[8]
参考资料
[1]
Jinja templating in Superset: https://www.bilibili.com/video/BV1Jq4y1i7Ny/[2]
Jinja2: http://jinja.pocoo.org/[3]
Jinja 模板: https://jinja.palletsprojects.com/en/2.11.x/[4]
sql_validators: https://github.com/apache/superset/tree/master/superset/sql_validators[5]
www.example.com: http://www.example.com/[6]
您可以在此处: https://github.com/apache/superset/blob/efd70077014cbed62e493372d33a2af5237eaadf/superset/jinja_context.py#L133-L148[7]
Jinja Templating in Superset: https://preset.io/events/jinja-templating-in-superset-apac-friendly/[8]
SQL Templating: https://superset.apache.org/docs/installation/sql-templating
到顶部