dbt快照调用列名获取宏报错:'None' has no attribute 'table'
解决dbt快照中使用动态列名宏出现的'None' has no attribute 'table'错误
问题根源
dbt快照的check_cols参数在编译阶段就会被评估,而你编写的宏如果依赖运行时上下文(比如model.table对象)或动态查询INFORMATION_SCHEMA,编译阶段这些对象/查询结果还未初始化,就会抛出None属性错误。硬编码列名能正常运行,是因为静态值在编译阶段可直接解析,无需依赖运行时资源。
解决方案
针对dbt-core1.5、dbt-bigquery1.5版本,提供三种可行修复方案:
方案1:直接指定表信息,避免依赖上下文对象
修改宏,通过传入明确的schema和表名查询INFORMATION_SCHEMA,同时添加编译阶段的占位值避免报错:
{% macro get_column_names_excl_meta_cols(schema_name, table_name, exclude_cols=['dbt_updated_at', 'dbt_valid_from', 'dbt_valid_to']) %} {% set query %} SELECT column_name FROM {{ schema_name }}.INFORMATION_SCHEMA.COLUMNS WHERE table_name = '{{ table_name }}' AND column_name NOT IN ({{ exclude_cols | map('quote') | join(', ') }}) ORDER BY ordinal_position {% endset %} {% set results = run_query(query) %} {% if execute %} {{ results.columns[0].values() | join(', ') }} {% else %} -- 编译阶段返回占位值,跳过语法检查 'placeholder_col' {% endif %} {% endmacro %}
快照中调用时传入明确的schema和表名:
{% snapshot store_hist %} {{ config( target_schema='snapshots', strategy='check', check_cols=[{{ get_column_names_excl_meta_cols('your_source_schema', 'store') }}], unique_key='store_id' ) }} select * from {{ source('my_source', 'store') }} {% endsnapshot %}
方案2:使用dbt内置适配器方法获取列信息(推荐)
用dbt内置的adapter.get_columns_in_relation替代手动查询INFORMATION_SCHEMA,该方法在编译阶段即可获取已存在表的列信息,更适配dbt上下文:
{% macro get_column_names_excl_meta_cols(relation, exclude_cols=['dbt_updated_at', 'dbt_valid_from', 'dbt_valid_to']) %} {% set cols = adapter.get_columns_in_relation(relation) %} {% set filtered_cols = cols | rejectattr('name', 'in', exclude_cols) | map(attribute='name') %} {% if execute %} {{ filtered_cols | join(', ') }} {% else %} '' {% endif %} {% endmacro %}
快照中先获取源关系再传入宏:
{% snapshot store_hist %} {% set source_relation = source('my_source', 'store') %} {{ config( target_schema='snapshots', strategy='check', check_cols=[{{ get_column_names_excl_meta_cols(source_relation) }}], unique_key='store_id' ) }} select * from {{ source_relation }} {% endsnapshot %}
方案3:预生成列名清单(适配快照编译逻辑)
如果上述方法仍有问题,可单独创建宏预生成列名列表,直接返回给check_cols:
- 创建预生成列名的宏:
{% macro get_store_check_cols() %} {% set cols = adapter.get_columns_in_relation(source('my_source', 'store')) %} {% set filtered_cols = cols | rejectattr('name', 'in', ['dbt_updated_at', 'dbt_valid_from', 'dbt_valid_to']) | map(attribute='name') %} {% if execute %} {{ filtered_cols | list }} {% else %} [] {% endif %} {% endmacro %}
- 快照中直接引用宏结果:
{% snapshot store_hist %} {{ config( target_schema='snapshots', strategy='check', check_cols={{ get_store_check_cols() }}, unique_key='store_id' ) }} select * from {{ source('my_source', 'store') }} {% endsnapshot %}
注意事项
- 确保源表/上游模型在运行快照前已存在(可先执行
dbt run再跑dbt snapshot),否则适配器方法无法获取列信息。 - dbt 1.5中
check_cols接受列名列表,宏需返回逗号分隔的字符串(方案1、2)或直接返回列表(方案3)。 - 所有动态逻辑必须添加
execute条件判断,并提供编译阶段的 fallback 值,避免语法解析错误。
内容的提问来源于stack exchange,提问作者VKarthik
相关产品推荐
相关产品推荐

