dbt 是数据 analytics 中批量数据处理的一个流行解决方案。虽然它采用了一种开源核心模式,这有时会限制社区功能在开源版本中的包含。别担心,有了opendbt(https://github.com/memiiso/opendbt),这些问题可以迎刃而解。opendbt 提供了一个完全开源的解决方案来解决这些问题。OpenDBT 是以 dbt-core 为基础构建的,添加了有用的功能但不改动其核心代码。
怎么做呢?这里有一个逐步示例。我们将展示如何使用OpenDBT激活自定义适配器,并在本地的dbt环境中运行Python模型。我们的重点是利用OpenDBT的特性来“注册自定义适配器类”。
这个问题使用 dbt core,可以在数据管道中使用 dbt SQL 或 Python(如 Spark、Snowpark)进行数据转换步骤,但这些转换仅限于 ELT 中的 T 部分。然而,无法执行 E(提取)和 L(加载)操作,例如从 web API 提取数据并加载到数据平台。这类任务通常需要使用 Python 编码在 dbt 之外完成,这会隐藏数据血缘和依赖关系。
使用 opendbt 导入外部数据
在这篇文章里,我们将展示如何使用opendbt来进行提取和加载。整个数据流程都是通过dbt来完成的。这表示所有的数据依赖关系都由dbt从头到尾定义和记录。
虽然dbt核心在数据转换(也就是ETL中的‘T’部分)方面功能强大,但它仅限于处理‘E’和‘L’步骤。它无法处理初始的数据提取和加载(也就是‘E’和‘L’步骤),比如从Web API抓取数据然后加载到数据平台中。
通常,这些早期步骤是在数据库转换工具 dbt 之外处理的,常常使用 Python 脚本。这样做可能会模糊数据的来源和依赖关系。
在这篇文章中,我们将看看如何在dbt里使用OpenDBT来进行提取和加载。通过这样做,我们可以保持端到端的数据流可见性和依赖关系的跟踪,确保数据管道更加稳健和透明。
创建你自己的自定义适配器
第一步:创建自定义适配器(adapter)
我们将从扩展现有的适配器(例如 DuckDBAdapter
)并根据我们的具体需求进行定制开始。我们将向适配器中添加一个新的方法,并使用 @available
装饰器使其可以通过 @available
装饰器在 dbt Jinja 模板中访问。此方法稍后将用于执行 dbt Python 模型。请参阅 完整适配器代码
class DuckDBAdapterV2Custom(DuckDBAdapter):
@available
def submit_local_python_job(self, parsed_model: Dict, compiled_code: str):
# 用于运行本地 Python 代码的 Python 方法,以及 dbt Python 模型
model_unique_id = parsed_model.get('unique_id')
__py_code = f"""
{compiled_code}
# 注意:这是本地 Python 执行,因此 session 为 None
model(dbt=dbtObj(None), session=None)
"""
with tempfile.NamedTemporaryFile(suffix=f'__{model_unique_id}.py', delete=False) as fp:
fp.write(__py_code.encode('utf-8'))
fp.close()
print(f"创建了临时的 py 文件 {fp.name}")
# 运行 Python 脚本
Utils.runcommand(command=['python', fp.name])
步骤 2:注册自定义适配器
OpenDBT 允许您通过设置 dbt_custom_adapter
变量来定义自定义适配器。要启用自定义适配器,请将以下配置添加到 dbt_project.yml
文件中:
vars:
dbt_custom_adapter: opendbt.examples.DuckDBAdapterV2Custom # 自定义的 dbt 适配器, 使用 DuckDBAdapterV2Custom
步骤三:创建一个执行Python代码的dbt宏
接下来,我们将创建一个名为 execute_python.sql
的 dbt 宏。此宏将把编译后的 Python 代码传递给我们在自定义适配器中新增的方法。参见 完整宏代码
{% materialization executepython, supported_languages=['python']%}
{%- set identifier = model['alias'] -%}
{%- set language = model['language'] -%}
{% set grant_config = config.get('grants') %}
{%- set old_relation = adapter.get_relation(database=database, schema=schema, identifier=identifier) -%}
{%- set target_relation = api.Relation.create(identifier=identifier,
schema=schema,
database=database, type='table') -%}
{{ run_hooks(pre_hooks) }}
{% call noop_statement(name='main', message='执行Python代码', code=compiled_code, rows_affected=-1, res=None) %}
{%- set res = adapter.submit_local_python_job(model, compiled_code) -%}
{% endcall %}
{{ run_hooks(post_hooks) }}
{% set should_revoke = should_revoke(old_relation, full_refresh_mode=True) %}
{% do apply_grants(target_relation, grant_config, should_revoke=should_revoke) %}
{% do persist_docs(target_relation, model) %}
{{ return({'关系表': [target_relation]}) }}
{% endmaterialization %}
第四步:运行本地的Python模型
准备好了,让我们跑起来!配置完成后,我们来本地跑一个Python模型。
创建您的Python模型!
将您的模型代码保存在名为 models/my_local_python_model.py
的文件里。在这个文件里,我们将简单地输出 Python 版本和平台信息以展示本地运行情况。
import os
import platform
def print_info():
_str = f"名称:{os.name}, 系统:{platform.system()} 发行版:{platform.release()}"
_str += f"\nPython 版:{platform.python_version()}, dbt 版本:{version.__version__}"
print(_str)
def model(dbt, session):
dbt.config(materialized="executepython")
print("==================================================")
print("========我是Python模型==========")
print("==================================================")
print_info()
print("==================================================")
print("===============让dbt再次伟大===============")
print("==================================================")
return None
步骤5:运行该Python模型:
dp = OpenDbtProject(project_dir='/dbt/project', profiles_dir='/dbt/project')
# 初始化dbt项目并运行特定模型
dp.run(command="run", args=['--select', 'my_local_python_model'])
这应该看起来像这样:
09:57:11 使用 dbt=1.8.7 运行
09:57:11 注册适配器:duckdb=1.8.4
09:57:12 无法进行部分解析,因为配置变量、配置文件或配置目标已更改
09:57:13 找到 4 个模型,4 个数据测试,418 个宏
09:57:13
09:57:15 并发:1 个线程(目标='dev')
09:57:15
09:57:15 1 个中的 1 个 开始 Python 模型 main.my_executepython_dbt_model ........ [运行]
创建临时 py 文件 /var/folders/3l/n5dbz15s68592fk76c31hth8ffmnng/T/tmp4l6hufwl__model.dbttest.my_executepython_dbt_model.py
==================================================
========本地执行的 Python 模型==========
==================================================
名称:posix,系统:Darwin 23.5.0
python 版本:3.9.19,dbt:1.8.7
==================================================
===============让 DBT 更加出色===============
==================================================
09:57:15 1 个中的 1 个 完成 Python 模型 main.my_executepython_dbt_model ... [在 0.51 秒内执行]
09:57:15
09:57:15 成功运行了 1 个 Python 模型,耗时 0 小时 0 分钟 2.53 秒 (2.53s)。
09:57:15
09:57:15 成功完成
09:57:15
09:57:15 完成。通过=1 警告=0 错误=0 跳过=0 总数=1
以下是执行流程的分解:
该完整代码、测试案例、文档等都在GitHub上可以找到,并且还包括其他特性。
总结与贡献该项目完全开源,并使用 Apache 2.0 许可证。它仍是一个年轻的项目,还有许多可以改进的地方。欢迎您尝试本项目,提供反馈,提交新功能请求或发送拉取请求。您可以在 GitHub 上的 opendbt 查看更多示例并开始尝试。
共同学习,写下你的评论
评论加载中...
作者其他优质文章