dbt init: создание первого проекта
После установки в предыдущем уроке у нас есть рабочий dbt в venv. Время создать первый проект.
Команда dbt init
Базовая форма:
dbt init <project_name>
Запускаем (из активированного venv):
dbt init jaffle_shop
Что произойдёт. dbt начнёт интерактивный диалог:
06:23:11 Running with dbt=1.10.3
Enter a name for your project (letters, digits, underscore): jaffle_shop
06:23:14
Your new dbt project "jaffle_shop" was created!
For more information on how to configure the base dbt_project.yml file,
and a list of all the available configurations, please consult the dbt documentation:
https://docs.getdbt.com/docs/configure-your-profile
Which database would you like to use?
[1] duckdb
(Don't see the one you want? https://docs.getdbt.com/docs/available-adapters)
Enter a number: 1
main: [1]: dev
path (path to the .duckdb file, relative to project root) [./dev.duckdb]:
schema (default schema that dbt will build objects in) [main]:
threads (1 or more) [1]: 4
dbt задаёт несколько вопросов:
| Вопрос | Что отвечать |
|---|---|
Enter a name for your project | Имя проекта, snake_case (например jaffle_shop, analytics, revenue_models) |
Which database would you like to use? | Выбираем duckdb (из списка установленных адаптеров) |
path (для DuckDB) | Путь к .duckdb файлу. По умолчанию ./dev.duckdb — то есть файл в корне проекта |
schema | Default схема внутри DuckDB. Стандартно — main |
threads | Параллельность. Для DuckDB 4-8 — нормально |
После последнего вопроса dbt создаст:
- Папку проекта
jaffle_shop/в текущей директории - Profile в
~/.dbt/profiles.ymlс записью под именем проекта
Видим финальный вывод:
Profile jaffle_shop written to /Users/lev/.dbt/profiles.yml using target's profile_template.yml and your supplied values. Run 'dbt debug' to validate the connection.
Что лежит в созданной папке
После dbt init структура папки:
Содержимое jaffle_shop/:
jaffle_shop/
├── README.md
├── analyses/ (пустая)
├── dbt_project.yml ← главный конфиг
├── macros/ (пустая)
├── models/
│ └── example/
│ ├── my_first_dbt_model.sql
│ ├── my_second_dbt_model.sql
│ └── schema.yml
├── seeds/ (пустая)
├── snapshots/ (пустая)
└── tests/ (пустая)
dbt 1.10 init не создаёт .gitignore автоматически в проекте (это в TODO для будущих релизов). Создайте сами:
cd jaffle_shop
cat > .gitignore <<'EOF'
target/
dbt_packages/
logs/
*.duckdb
*.duckdb.wal
*.duckdb.tmp
EOF
Что исключаем и почему:
target/— compiled SQL, manifest, catalog. Регенерируется каждымdbt run.dbt_packages/— установленные packages (какnode_modules). Регенерируется черезdbt deps.logs/— dbt-логи. Локальный артефакт.*.duckdb— сам файл базы. Тоже локальный артефакт.
Не коммитьте *.duckdb файлы в git. Это бинарные файлы, иногда большие, и они меняются каждый dbt run — git будет тормозить. Если нужна shared база между разработчиками — используйте MotherDuck (managed DuckDB cloud) или передавайте через S3.
models/example/: что внутри
dbt init создаёт три файла-примера в models/example/:
my_first_dbt_model.sql:
/*
Welcome to your first dbt model!
Did you know that you can also configure models directly within SQL files?
This will override configurations stated in dbt_project.yml
Try changing "table" to "view" below
*/
{{ config(materialized='table') }}
with source_data as (
select 1 as id
union all
select null as id
)
select *
from source_data
/*
Uncomment the line below to remove records with null `id` values
*/
-- where id is not null
Что здесь:
{{ config(materialized='table') }}— Jinja-блок, который задаёт materialization. В этом проекте — таблица.- Запрос — два литеральных значения через UNION ALL.
- Комментарии-инструкции от dbt Labs про возможность поменять
'table'на'view'.
my_second_dbt_model.sql:
-- Use the `ref` function to select from other models
select *
from {{ ref('my_first_dbt_model') }}
where id = 1
Это первый пример ref() — модель ссылается на my_first_dbt_model и выбирает только id = 1.
schema.yml:
version: 2
models:
- name: my_first_dbt_model
description: "A starter dbt model"
columns:
- name: id
description: "The primary key for this table"
data_tests:
- unique
- not_null
- name: my_second_dbt_model
description: "A starter dbt model"
columns:
- name: id
description: "The primary key for this table"
data_tests:
- relationships:
to: ref('my_first_dbt_model')
field: id
Это первый пример тестов в YAML:
uniqueиnot_nullнаmy_first_dbt_model.idrelationships(foreign key) —my_second_dbt_model.idдолжен существовать вmy_first_dbt_model.id
Эти модели — учебные. Мы их позже удалим и заменим на свои.
Первый dbt debug
После dbt init сразу запускаем проверку подключения:
cd jaffle_shop
dbt debug
Ожидаемый вывод:
06:30:11 Running with dbt=1.10.3
06:30:11 dbt version: 1.10.3
06:30:11 python version: 3.12.4
06:30:11 python path: /Users/lev/dbt-projects/.venv/bin/python3.12
06:30:11 os info: macOS-14.5-arm64-arm-64bit
06:30:11 Using profiles dir at /Users/lev/.dbt
06:30:11 Using profiles.yml file at /Users/lev/.dbt/profiles.yml
06:30:11 Using dbt_project.yml file at /Users/lev/dbt-projects/jaffle_shop/dbt_project.yml
06:30:11 adapter type: duckdb
06:30:11 adapter version: 1.10.1
06:30:11 Configuration:
06:30:11 profiles.yml file [OK found and valid]
06:30:11 dbt_project.yml file [OK found and valid]
06:30:11 Required dependencies:
06:30:11 - git [OK found]
06:30:11 Connection:
06:30:11 database: dev
06:30:11 schema: main
06:30:11 path: ./dev.duckdb
06:30:11 Registered adapter: duckdb=1.10.1
06:30:11 Connection test: [OK connection ok]
06:30:11 All checks passed!
Что проверил dbt debug:
- Версия dbt и Python
- Расположение
profiles.yml(по умолчанию~/.dbt/profiles.yml) - Расположение
dbt_project.yml(в папке проекта) - Корректность конфигурации YAML
- Наличие
gitв PATH - Connection test — реальная попытка подключиться к warehouse и выполнить простой запрос
Для DuckDB connection test тривиален — это просто создание файла dev.duckdb. Для Snowflake / BigQuery это реально подключение с credentials и проверка прав.
Первый dbt run
Если dbt debug прошёл, запускаем dbt run:
dbt run
Ожидаемый вывод:
06:31:23 Running with dbt=1.10.3
06:31:23 Registered adapter: duckdb=1.10.1
06:31:23 Found 2 models, 4 data tests, 408 macros
06:31:23
06:31:23 Concurrency: 4 threads (target='dev')
06:31:23
06:31:23 1 of 2 START sql table model main.my_first_dbt_model ........................... [RUN]
06:31:23 1 of 2 OK created sql table model main.my_first_dbt_model ...................... [OK in 0.04s]
06:31:23 2 of 2 START sql view model main.my_second_dbt_model ........................... [RUN]
06:31:23 2 of 2 OK created sql view model main.my_second_dbt_model ...................... [OK in 0.03s]
06:31:23
06:31:23 Finished running 1 table model, 1 view model in 0 hours 0 minutes and 0.08 seconds (0.08s).
06:31:23
06:31:23 Completed successfully
06:31:23
06:31:23 Done. PASS=2 WARN=0 ERROR=0 SKIP=0 TOTAL=2
Что произошло:
- dbt прочитал
dbt_project.ymlи нашёл 2 модели - Построил DAG:
my_first_dbt_model->my_second_dbt_model(черезref()) - Запустил их в правильном порядке
my_first_dbt_modelматериализован какtable(через{{ config() }})my_second_dbt_model— какview(default)- В
./dev.duckdbтеперь есть две таблицы/view в схемеmain
Проверим через DuckDB CLI:
duckdb ./dev.duckdb
.tables
-- my_first_dbt_model my_second_dbt_model
SELECT * FROM my_first_dbt_model;
-- ┌───────┐
-- │ id │
-- │ int32 │
-- ├───────┤
-- │ 1 │
-- │ NULL │
-- └───────┘
Где живут profiles
dbt init создал/обновил ~/.dbt/profiles.yml. Откроем:
cat ~/.dbt/profiles.yml
Содержимое:
jaffle_shop:
outputs:
dev:
type: duckdb
path: ./dev.duckdb
schema: main
threads: 4
target: dev
Это и есть profile — настройки подключения к warehouse для проекта jaffle_shop. Связь между dbt_project.yml и profiles.yml — через имя profile (которое равно имени проекта по умолчанию). Подробно про profiles — следующий урок.
~/.dbt/profiles.yml живёт вне проекта, в home директории пользователя. Это намеренно — там хранятся credentials, и их не должно быть в git-репо. На production обычно используются env vars вместо литералов.
Альтернатива: skip-profile-setup
Если уже знаете, что и куда писать в profiles.yml, можно пропустить интерактив:
dbt init jaffle_shop --skip-profile-setup
Это создаст только структуру папки проекта, без модификации ~/.dbt/profiles.yml. Profile нужно создать вручную.
Удобно когда:
- В команде уже стандарт profiles, и interactive override не нужен
- Делаете bootstrap в Docker
- В CI скрипте
Чистка после dbt init: первые правки
После dbt init я обычно делаю несколько cleanup-шагов:
cd jaffle_shop
# 1. Удалить examples
rm -rf models/example/
# 2. Создать правильный .gitignore (см. выше)
# 3. Создать структуру для будущих моделей:
mkdir -p models/staging models/intermediate models/marts
# 4. Заменить README на свой
echo "# jaffle_shop\n\nA dbt project for learning dbt-core 1.10 on DuckDB.\n\n## Setup\n\n\`\`\`bash\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -r requirements.txt\ndbt deps\ndbt build\n\`\`\`" > README.md
# 5. Создать requirements.txt
pip freeze > requirements.txt
После этих шагов у вас чистая база проекта, готовая принять реальные модели.
dbt_project.yml: первый взгляд
Откроем главный конфиг:
cat dbt_project.yml
Сокращённое содержимое (есть много комментариев):
name: 'jaffle_shop'
version: '1.0.0'
profile: 'jaffle_shop'
model-paths: ["models"]
analysis-paths: ["analyses"]
test-paths: ["tests"]
seed-paths: ["seeds"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]
clean-targets:
- "target"
- "dbt_packages"
models:
jaffle_shop:
example:
+materialized: view
Ключевые поля:
name— имя проекта (snake_case, без дефисов)profile— имя профиля вprofiles.yml, который использоватьmodel-pathsиseed-paths/test-paths/macro-paths/snapshot-paths— где dbt ищет соответствующие файлыclean-targets— какие папки удаляетdbt cleanmodels:— конфиги по подпапкам. Здесьjaffle_shop.example.+materialized: view— все модели вmodels/example/будут view (если только не override через{{ config() }}в .sql).
Этот файл будем редактировать в каждом следующем уроке. Сейчас достаточно знать, что он есть и где он.
Попробуй сам
- Создайте проект
dbt init learning_dbt(или любое имя) cd learning_dbt && dbt debug— connection test должен пройтиdbt run— две example-модели должны построиться- Откройте
./dev.duckdbчерезduckdbCLI (если установлен —brew install duckdbна macOS) или прочитайте через Python:
import duckdb
con = duckdb.connect('./dev.duckdb')
print(con.execute("SELECT * FROM my_first_dbt_model").fetchall())
# [(1,), (None,)]
- Удалите
models/example/, добавьте.gitignore, и инициализируйте git:git init && git add . && git commit -m "init"
После этого у вас чистый проект, готовый к Лабораторной 1.