dbt Workflows¶
Using dbt directly for transformation development and debugging.
Overview¶
Dango uses dbt for data transformations. While dango run handles typical workflows, direct dbt access is useful for:
- Developing and debugging specific models
- Running tests
- Generating documentation
- Advanced selector patterns
dbt Project Structure¶
Dango creates and manages a dbt project in the dbt/ directory:
dbt/
├── dbt_project.yml # Project configuration
├── profiles.yml # Connection profiles (auto-generated)
├── models/
│ ├── staging/ # Auto-generated from sources
│ │ ├── sources.yml # Source definitions
│ │ └── stg_*.sql # Staging models
│ ├── intermediate/ # Your business logic
│ └── marts/ # Final tables
├── snapshots/ # SCD Type 2 snapshots (dango snapshot add)
├── macros/ # Reusable SQL
├── tests/ # Custom tests
└── seeds/ # Static data
Running dbt Commands¶
Navigate to dbt Directory¶
# All dbt commands must run from the dbt directory
cd dbt
# Or use --project-dir flag
dbt run --project-dir ./dbt
Common Commands¶
# Run all models
dbt run
# Run specific model
dbt run --select my_model
# Run model and its downstream dependencies
dbt run --select my_model+
# Run model and its upstream dependencies
dbt run --select +my_model
# Run entire lineage
dbt run --select +my_model+
DbtLock Not Acquired
Running dbt run directly does not acquire Dango's DbtLock. If a sync or scheduled job runs concurrently, both processes write to DuckDB simultaneously, which can cause data corruption. Use dango run or dango dev instead for safe execution, or ensure no syncs are running.
Dev Mode¶
The dango dev command provides safe, isolated dbt development by running against a copy of your database.
# Run dbt against a dev copy of warehouse.duckdb
dango dev
# Compare row counts between dev and production
dango dev --diff
# Clean up dev artifacts when done
dango dev clean
Dev mode copies your database to .dango/dev/warehouse_dev.duckdb and runs all transformations there. Your production database is untouched — experiment freely.
For full details on the dev workflow, see Dev Workflow.
Snapshots (SCD Type 2)¶
Track historical changes in your source data using dbt snapshots. Dango provides a wizard to generate snapshot definitions:
# Add a new snapshot (interactive wizard)
dango snapshot add
# List all configured snapshots
dango snapshot list
# Run snapshots to capture current state
dango snapshot run
# Run a specific snapshot
dango snapshot run --select my_snapshot
# Create a read-only DuckDB copy for notebooks
dango snapshot db
The wizard generates a dbt snapshot SQL file in dbt/snapshots/ with your chosen strategy:
- Timestamp — track changes via an
updated_atcolumn - Check — track changes by comparing all (or selected) columns
For full details, see Snapshots.
Development Workflow¶
1. Create a New Model¶
-- dbt/models/marts/fct_daily_revenue.sql
{{ config(materialized='table') }}
select
date_trunc('day', order_date) as day,
sum(amount) as total_revenue,
count(distinct order_id) as order_count
from {{ ref('stg_orders') }}
group by 1
2. Compile to Check SQL¶
# Compile without running (validates SQL)
dbt compile --select fct_daily_revenue
# View compiled SQL
cat target/compiled/my_project/models/marts/fct_daily_revenue.sql
3. Run the Model¶
# Run single model
dbt run --select fct_daily_revenue
# Run with full refresh (rebuild table)
dbt run --select fct_daily_revenue --full-refresh
4. Test the Model¶
Selector Patterns¶
dbt selectors let you target specific models:
| Pattern | Description | Example |
|---|---|---|
model_name | Single model | dbt run --select fct_revenue |
+model_name | Model + upstream | dbt run --select +fct_revenue |
model_name+ | Model + downstream | dbt run --select fct_revenue+ |
path/to/models | All models in path | dbt run --select marts/ |
tag:my_tag | Models with tag | dbt run --select tag:daily |
source:name | Source-related models | dbt run --select source:stripe+ |
Exclude Patterns¶
# Run everything except staging
dbt run --exclude staging/
# Run marts except one model
dbt run --select marts/ --exclude fct_large_table
Testing¶
Built-in Tests¶
Add tests in schema YAML files:
# dbt/models/marts/schema.yml
version: 2
models:
- name: fct_daily_revenue
description: Daily revenue aggregations
columns:
- name: day
description: Date of revenue
tests:
- not_null
- unique
- name: total_revenue
tests:
- not_null
Custom Tests¶
-- dbt/tests/assert_positive_revenue.sql
select *
from {{ ref('fct_daily_revenue') }}
where total_revenue < 0
Run Tests¶
# All tests
dbt test
# Tests for specific model
dbt test --select fct_daily_revenue
# Only schema tests
dbt test --select test_type:schema
# Only data tests
dbt test --select test_type:data
Documentation¶
Generate Docs¶
Access via Dango¶
# Dango also serves dbt docs
dango docs
# Or access directly at the default port
open http://localhost:8081
Add Descriptions¶
# dbt/models/marts/schema.yml
version: 2
models:
- name: fct_daily_revenue
description: |
Daily revenue metrics aggregated from orders.
**Grain**: One row per day
**Update frequency**: After each sync
columns:
- name: day
description: Calendar date (truncated to day)
- name: total_revenue
description: Sum of all order amounts for the day
Debugging¶
View Compiled SQL¶
# Compile model to see actual SQL
dbt compile --select my_model
# Output is in target/compiled/
cat target/compiled/*/models/**/my_model.sql
Debug Configuration¶
# Show dbt configuration
dbt debug
# Example output:
# profiles.yml found at ./profiles.yml
# dbt_project.yml found at ./dbt_project.yml
# Connection test: OK
Log Verbosity¶
# Run with debug logging
dbt run --select my_model --debug
# Or set log level
dbt run --select my_model --log-level debug
Materializations¶
Dango uses table materialization for all auto-generated models to ensure reliable Metabase compatibility. Views can cause query issues in Metabase.
Configure Materialization¶
-- Table (default for all Dango models)
{{ config(materialized='table') }}
-- Incremental (for large fact tables)
{{ config(
materialized='incremental',
unique_key='id'
) }}
Why Tables, Not Views
Dango auto-generates staging models as tables (not views) because Metabase works more reliably with materialized tables. While views rebuild faster during development, they can cause query timeouts and missing data in Metabase dashboards.
Incremental Models¶
-- dbt/models/marts/fct_orders.sql
{{ config(
materialized='incremental',
unique_key='order_id'
) }}
select
order_id,
customer_id,
amount,
created_at
from {{ ref('stg_orders') }}
{% if is_incremental() %}
where created_at > (select max(created_at) from {{ this }})
{% endif %}
Working with Profiles¶
Profile Location¶
Dango auto-generates profiles.yml:
# dbt/profiles.yml
my_project:
target: dev
outputs:
dev:
type: duckdb
path: ../data/warehouse.duckdb
schema: main
Multiple Environments¶
# dbt/profiles.yml
my_project:
target: dev
outputs:
dev:
type: duckdb
path: ../data/warehouse.duckdb
prod:
type: duckdb
path: /var/data/warehouse.duckdb
Useful Commands Reference¶
| Command | Description |
|---|---|
dbt run | Run all models |
dbt run --select model | Run specific model |
dbt test | Run all tests |
dbt compile | Compile SQL without running |
dbt docs generate | Generate documentation |
dbt docs serve | Serve docs locally |
dbt debug | Test configuration |
dbt deps | Install packages |
dbt clean | Remove compiled files |
dbt seed | Load seed data |
dbt snapshot | Run snapshots |
Next Steps¶
- Staging Models - Auto-generated models
- Custom Models - Building transformations
- Testing - Data quality tests
- Dev Workflow - Safe development with
dango dev - Snapshots - SCD Type 2 change tracking