为了账号安全,请及时绑定邮箱和手机立即绑定

用opendbt扩展dbt的功能:轻松处理数据提取和加载步骤

dbt 是数据 analytics 中批量数据处理的一个流行解决方案。虽然它采用了一种开源核心模式,这有时会限制社区功能在开源版本中的包含。别担心,有了opendbthttps://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 查看更多示例并开始尝试。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消