dbt com Python: Analytics Engineering para Dados Confiáveis
Aprenda como usar dbt com Python em projetos de analytics engineering, testes de dados, modelos SQL, DuckDB, CI e portfólio para vagas de dados.
Quem trabalha com Python em dados costuma começar com scripts: um arquivo lê CSV, outro consulta uma API, outro limpa colunas, outro gera uma planilha. Esse caminho é natural para aprender, mas fica difícil de manter quando a análise vira rotina do time. Sem uma camada organizada de transformação, cada dashboard passa a ter sua própria regra de negócio, os nomes de colunas mudam sem aviso, o mesmo cálculo aparece em três lugares e ninguém sabe se a tabela final ainda está confiável.
É aí que entra o dbt. A ferramenta nasceu no mundo SQL, mas conversa muito bem com projetos Python porque resolve a parte de modelagem, documentação, testes e linhagem dos dados. Em vez de tratar transformação como um monte de consultas soltas, você descreve modelos versionados, dependências entre tabelas, testes automáticos e documentação que pode ser revisada no Git. Para quem busca vaga em engenharia de dados, analytics engineering, BI, RevOps ou data quality, dbt virou um sinal forte de maturidade.
Neste guia, vamos montar o raciocínio prático: quando usar dbt, onde Python entra, como estruturar um projeto simples com DuckDB, que testes escrever e como transformar isso em evidência de portfólio. Se você está construindo a base, leia também Airflow com Python, Great Expectations com Python, DuckDB para analytics, Pandera em pipelines e vagas Python no Brasil.
O que é dbt
dbt significa data build tool. Ele não substitui Python, Airflow, Pandas ou o banco de dados. O papel do dbt é transformar dados que já chegaram a um banco, warehouse ou engine analítica em tabelas confiáveis para análise.
Na prática, você escreve modelos SQL em arquivos versionados. Cada modelo vira uma tabela ou view. O dbt entende dependências entre modelos usando ref(), executa tudo na ordem correta, roda testes, gera documentação e deixa claro de onde cada métrica veio.
Um fluxo típico fica assim:
- Python extrai dados de APIs, arquivos ou sistemas internos.
- Python grava dados brutos em Postgres, BigQuery, Snowflake, DuckDB ou outro destino.
- dbt transforma os dados brutos em tabelas limpas, intermediárias e finais.
- testes do dbt verificam chaves, nulos, valores aceitos e relações.
- dashboards, notebooks e relatórios consomem as tabelas finais.
Essa separação evita um erro comum: colocar regra de negócio crítica dentro de um notebook isolado. O notebook é ótimo para exploração; dbt é melhor para transformar a lógica validada em contrato reutilizável.
Quando dbt faz sentido
Use dbt quando seus dados precisam ser explicáveis, repetíveis e revisáveis. Alguns sinais:
| Situação | Como dbt ajuda |
|---|---|
| Dashboard com métricas recorrentes | centraliza a regra de cálculo em modelos versionados |
| Pipeline com várias camadas | organiza staging, intermediate e marts |
| Time discutindo número diferente | documenta origem, filtro e transformação de cada métrica |
| Dados com risco de quebra | adiciona testes automáticos no fluxo |
| Projeto de portfólio | mostra SQL, Git, modelagem, testes e documentação juntos |
Se você só precisa limpar um CSV uma vez, dbt pode ser excesso. Se a transformação precisa rodar todo dia, alimentar dashboard, ser revisada por outra pessoa ou virar parte de uma análise de negócio, dbt começa a valer muito.
Em vagas brasileiras, dbt aparece junto de Python, SQL, BigQuery, Snowflake, Databricks, Airflow e ferramentas de BI. A combinação é importante: Python mostra capacidade de automação e integração; dbt mostra capacidade de modelar dados para consumo confiável.
Onde Python entra no projeto
dbt não elimina Python. Ele deixa Python focado no que Python faz melhor:
- consumir APIs e lidar com autenticação;
- baixar arquivos de dados públicos;
- fazer validações específicas antes da carga;
- converter formatos como JSON, CSV, Excel e Parquet;
- agendar ou disparar processos auxiliares;
- criar testes complementares com pytest;
- gerar pequenos relatórios ou artefatos de monitoramento.
Imagine um projeto de vendas. Python busca pedidos em uma API, salva raw_orders em DuckDB ou Postgres e registra logs. dbt cria stg_orders, int_customer_revenue e mart_sales_daily. O dashboard usa mart_sales_daily, não o JSON bruto da API.
Esse desenho deixa o projeto mais fácil de explicar em entrevista: “Python faz ingestão e automação; dbt faz transformação, testes e documentação; o BI consome modelos finais”. É uma resposta muito mais profissional do que “tenho um script que gera uma planilha”.
Instalando dbt com DuckDB
Para estudar localmente, DuckDB é uma escolha excelente porque não exige servidor. Crie um projeto:
uv init analytics-dbt-python
cd analytics-dbt-python
uv add dbt-duckdb duckdb pandas pytest
Com pip, o equivalente é:
python -m venv .venv
source .venv/bin/activate
pip install dbt-duckdb duckdb pandas pytest
Inicialize o dbt:
dbt init analytics_python
Uma estrutura simples pode ficar assim:
analytics-dbt-python/
data/
pedidos.csv
scripts/
carregar_raw.py
analytics_python/
dbt_project.yml
models/
staging/
stg_pedidos.sql
schema.yml
marts/
mart_vendas_diarias.sql
schema.yml
tests/
test_raw_loader.py
O ponto principal é separar responsabilidades. scripts/ contém Python para carga. models/ contém transformações dbt. tests/ cobre código Python. Os testes de dados ficam em schema.yml, perto dos modelos.
Carregando dados brutos com Python
Vamos imaginar um CSV de pedidos:
pedido_id,cliente_id,uf,valor,status,data_pedido
1001,10,SP,129.90,pago,2026-05-20
1002,11,MG,89.50,enviado,2026-05-20
1003,10,SP,240.00,cancelado,2026-05-21
Um carregador mínimo em Python poderia criar uma tabela bruta no DuckDB:
from pathlib import Path
import duckdb
import pandas as pd
BASE_DIR = Path(__file__).resolve().parents[1]
DB_PATH = BASE_DIR / "warehouse.duckdb"
CSV_PATH = BASE_DIR / "data" / "pedidos.csv"
def carregar_pedidos() -> None:
df = pd.read_csv(CSV_PATH, parse_dates=["data_pedido"])
with duckdb.connect(DB_PATH) as con:
con.execute("CREATE SCHEMA IF NOT EXISTS raw")
con.execute("CREATE OR REPLACE TABLE raw.pedidos AS SELECT * FROM df")
if __name__ == "__main__":
carregar_pedidos()
Esse código não tenta resolver toda a regra de negócio. Ele só garante que os dados brutos entrem em um lugar consultável. A transformação vem depois, com dbt.
Criando o primeiro modelo staging
Modelos de staging normalizam nomes, tipos e pequenos ajustes. Em models/staging/stg_pedidos.sql:
with source as (
select * from raw.pedidos
),
renamed as (
select
cast(pedido_id as integer) as pedido_id,
cast(cliente_id as integer) as cliente_id,
upper(uf) as uf,
cast(valor as decimal(12, 2)) as valor,
lower(status) as status,
cast(data_pedido as date) as data_pedido
from source
)
select * from renamed
O staging não deve virar um labirinto de regra de negócio. Ele prepara os dados para que os próximos modelos tenham nomes consistentes e tipos previsíveis. Se uma coluna vem como texto mas representa dinheiro, este é um bom lugar para converter. Se uma UF vem em minúsculo, normalize aqui.
Criando um mart de vendas diárias
Agora crie um modelo final em models/marts/mart_vendas_diarias.sql:
with pedidos as (
select * from {{ ref('stg_pedidos') }}
),
agregado as (
select
data_pedido,
uf,
count(*) as total_pedidos,
sum(case when status != 'cancelado' then valor else 0 end) as receita_liquida,
sum(case when status = 'cancelado' then 1 else 0 end) as pedidos_cancelados
from pedidos
group by 1, 2
)
select * from agregado
A função ref('stg_pedidos') é uma das partes mais importantes do dbt. Ela cria dependência explícita entre modelos. O dbt sabe que mart_vendas_diarias depende de stg_pedidos, então executa na ordem certa e gera linhagem na documentação.
Esse modelo já tem cara de tabela para BI. Em vez de cada dashboard recalcular cancelamento e receita, a regra fica centralizada. Se amanhã a empresa decidir que estornado também deve ser excluído da receita, você altera um modelo, revisa no Git e roda os testes.
Testes de dados com schema.yml
dbt permite declarar testes genéricos em YAML. Para staging:
version: 2
models:
- name: stg_pedidos
description: "Pedidos normalizados a partir da camada raw."
columns:
- name: pedido_id
tests:
- not_null
- unique
- name: cliente_id
tests:
- not_null
- name: uf
tests:
- not_null
- accepted_values:
values: ['AC', 'AL', 'AM', 'BA', 'CE', 'DF', 'ES', 'GO', 'MG', 'PE', 'PR', 'RJ', 'RS', 'SC', 'SP']
- name: status
tests:
- accepted_values:
values: ['pago', 'enviado', 'cancelado']
Para o mart:
version: 2
models:
- name: mart_vendas_diarias
description: "Receita líquida e volume de pedidos por data e UF."
columns:
- name: data_pedido
tests:
- not_null
- name: total_pedidos
tests:
- not_null
Esses testes parecem simples, mas capturam problemas comuns: pedido duplicado, UF inválida, status inesperado e campo obrigatório vazio. Para um projeto de portfólio, o mais importante é mostrar a mentalidade: dados têm contrato, e o contrato roda automaticamente.
Rodando e documentando
Com os dados carregados, execute:
dbt run
dbt test
dbt docs generate
dbt docs serve
O dbt run materializa os modelos. O dbt test valida contratos. O dbt docs generate cria documentação com descrições, colunas, testes e linhagem. O dbt docs serve abre a documentação local.
Em um README de portfólio, inclua comandos reproduzíveis:
python scripts/carregar_raw.py
cd analytics_python
dbt run
dbt test
dbt docs generate
Também vale salvar prints ou descrever a linhagem: raw.pedidos -> stg_pedidos -> mart_vendas_diarias. Isso ajuda recrutadores e pessoas técnicas a entenderem o desenho sem abrir todos os arquivos.
CI simples para dbt
Um projeto sério não depende apenas de rodar localmente. Você pode adicionar um workflow de CI que instala dependências, carrega dados de exemplo e roda testes:
name: dbt checks
on: [push, pull_request]
jobs:
dbt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uv sync
- run: uv run python scripts/carregar_raw.py
- run: uv run dbt run --project-dir analytics_python
- run: uv run dbt test --project-dir analytics_python
Mesmo que seu projeto seja pequeno, esse tipo de automação comunica cuidado profissional. Em vaga de dados, CI com dbt mostra que você sabe proteger transformação crítica antes de publicar dashboard ou entregar métrica.
dbt, Great Expectations e Pandera
dbt testa dados dentro da camada analítica. Great Expectations e Pandera podem complementar em outros pontos.
Uma divisão prática:
| Ferramenta | Melhor uso |
|---|---|
| Pandera | validar DataFrames no código Python |
| Great Expectations | suítes ricas, documentação de qualidade e checkpoints |
| dbt tests | contratos dos modelos transformados no warehouse |
| pytest | comportamento do código Python de ingestão e utilitários |
Não use todas as ferramentas só para parecer avançado. Use quando cada uma protege uma etapa real. Em um projeto enxuto, Python + dbt tests pode ser suficiente. Em um pipeline maior, Pandera pode validar antes da carga e dbt pode validar depois da transformação.
Ideias de projeto para o Brasil
dbt fica mais forte quando o exemplo tem contexto de negócio. Algumas ideias boas para portfólio:
- dados públicos do Banco Central com séries econômicas por mês;
- indicadores de vagas Python por tecnologia, senioridade e modelo remoto;
- análise de vendas fictícias por UF, canal e categoria;
- pipeline de leads de CRM com estágio do funil e receita esperada;
- dados de mobilidade, clima ou turismo por cidade;
- acompanhamento de métricas de produto a partir de eventos anônimos.
O segredo é não entregar apenas gráfico. Entregue camadas: raw, staging, marts, testes, documentação e README. Se usar dados públicos, explique origem, atualização, limitações e licença. Se usar dados sintéticos, deixe claro que são fictícios.
Erros comuns
Alguns erros aparecem muito em projetos iniciantes:
- colocar regra de negócio complexa no script de carga e deixar dbt vazio;
- criar modelos finais direto da camada raw, sem staging;
- não escrever testes de unicidade, nulos e valores aceitos;
- versionar banco local grande no Git;
- usar nomes genéricos como
tabela_final.sql; - não documentar colunas importantes;
- tratar dashboard como fonte de verdade em vez de consumir marts versionados.
Outro erro é vender dbt como ferramenta Python. dbt não é uma biblioteca para manipular DataFrame. Ele é uma ferramenta de transformação analítica. A força está na combinação: Python integra e automatiza, SQL modela, dbt testa e documenta.
Como apresentar no currículo
Em vez de escrever apenas “conhecimento em dbt”, descreva resultado:
Projeto de analytics engineering com Python, DuckDB e dbt: ingestão de dados brutos, modelos staging e marts, testes de unicidade/nulos/valores aceitos, documentação automática e CI com dbt run/test.
Essa frase mostra stack, processo e evidência. Se o projeto tiver dashboard, melhor ainda, mas o diferencial é a confiabilidade. Muitas pessoas conseguem montar um gráfico; menos pessoas conseguem explicar de onde o número veio e como ele é testado.
Para conectar com carreira, publique o repositório com README claro, comandos de reprodução e uma seção “decisões técnicas”. Explique por que escolheu DuckDB local, onde entraria Airflow em produção, quais testes protegem o pipeline e como você evoluiria para BigQuery, Snowflake ou Postgres.
Próximos passos
Depois deste primeiro projeto, evolua em camadas:
- troque CSV por uma API real ou dados públicos brasileiros;
- adicione um modelo intermediário com regra de negócio mais rica;
- configure seeds para tabelas de referência, como lista de UFs;
- gere documentação e publique prints no README;
- adicione CI com
dbt runedbt test; - conecte um dashboard simples ao mart final.
Se você já estudou ETL com Python e Airflow, dbt é o próximo passo natural para transformar dados carregados em produtos analíticos confiáveis. Para projetos de vaga, ele ajuda a contar uma história completa: ingestão, transformação, qualidade, documentação e impacto de negócio.
Equipe Python Brasil
Contribuidor do Python Brasil — Aprenda Python em Português