Learning Platform
Глоссарий Troubleshooting
Урок 04.02 · 14 мин
Начальный
dbt-initproject-skeletonscaffold

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 — то есть файл в корне проекта
schemaDefault схема внутри DuckDB. Стандартно — main
threadsПараллельность. Для DuckDB 4-8 — нормально

После последнего вопроса dbt создаст:

  1. Папку проекта jaffle_shop/ в текущей директории
  2. 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 структура папки:

Skeleton dbt-проекта
dbt_project.ymlГлавный конфиг проекта: name, version, profile, paths, model configs
README.mdБазовый README, который вы должны переписать
.gitignorePre-populated .gitignore с правильными исключениями (target/, dbt_packages/, logs/)
models/Где живут SQL-модели. dbt init создаёт пример example/
seeds/Где живут CSV-файлы для загрузки как таблицы
snapshots/Где живут snapshot-определения (SCD2)
tests/Где живут singular tests (отдельные SQL-файлы тестов)
macros/Где живут Jinja-macros (переиспользуемые куски логики)
analyses/Где живут ad-hoc analyses (компилируются, но не выполняются)

Содержимое 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 — сам файл базы. Тоже локальный артефакт.
WARNING

Не коммитьте *.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.id
  • relationships (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:

  1. Версия dbt и Python
  2. Расположение profiles.yml (по умолчанию ~/.dbt/profiles.yml)
  3. Расположение dbt_project.yml (в папке проекта)
  4. Корректность конфигурации YAML
  5. Наличие git в PATH
  6. 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

Что произошло:

  1. dbt прочитал dbt_project.yml и нашёл 2 модели
  2. Построил DAG: my_first_dbt_model -> my_second_dbt_model (через ref())
  3. Запустил их в правильном порядке
  4. my_first_dbt_model материализован как table (через {{ config() }})
  5. my_second_dbt_model — как view (default)
  6. В ./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 — следующий урок.

NOTE

~/.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 clean
  • models: — конфиги по подпапкам. Здесь jaffle_shop.example.+materialized: view — все модели в models/example/ будут view (если только не override через {{ config() }} в .sql).

Этот файл будем редактировать в каждом следующем уроке. Сейчас достаточно знать, что он есть и где он.


Попробуй сам

  1. Создайте проект dbt init learning_dbt (или любое имя)
  2. cd learning_dbt && dbt debug — connection test должен пройти
  3. dbt run — две example-модели должны построиться
  4. Откройте ./dev.duckdb через duckdb CLI (если установлен — brew install duckdb на macOS) или прочитайте через Python:
import duckdb
con = duckdb.connect('./dev.duckdb')
print(con.execute("SELECT * FROM my_first_dbt_model").fetchall())
# [(1,), (None,)]
  1. Удалите models/example/, добавьте .gitignore, и инициализируйте git: git init && git add . && git commit -m "init"

После этого у вас чистый проект, готовый к Лабораторной 1.


Проверка знанийKnowledge check
Где dbt init размещает profiles.yml и почему этот файл лежит вне папки проекта?
ОтветAnswer
dbt init размещает profiles.yml в ~/.dbt/profiles.yml (то есть в home-директории пользователя, в скрытой папке .dbt), а не внутри папки проекта. Это сделано намеренно по двум причинам: (1) безопасность — в profiles.yml хранятся credentials (passwords, API keys для warehouses); если бы файл лежал в проекте, его было бы легко случайно закоммитить в git и слить секреты публично; (2) переиспользование — один пользователь может работать с несколькими dbt-проектами, и profiles.yml может содержать profiles для всех них в одном месте; project.yml ссылается на profile по имени, и dbt находит его в ~/.dbt/. Альтернативный способ — переопределить путь через переменную окружения DBT_PROFILES_DIR или флаг --profiles-dir, что часто используется в CI для подгрузки project-specific profile.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 6. Команда `dbt init jaffle_shop` создала две вещи. Какие?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4