var(): параметры проекта через dbt_project.yml и —vars
Вы уже несколько раз видели в курсе вызовы {{ var('start_date') }} — функция var() извлекает значение параметра проекта, который можно задать в dbt_project.yml или передать через CLI. Это базовый механизм dbt для параметризации: разные команды/окружения могут запускать одну и ту же модель с разными значениями, без правки SQL.
В этом уроке — полная картина: как объявлять, как использовать, как переопределять, лучшие практики.
Зачем нужны vars
Типовые сценарии:
- Конфигурация даты для backfill. Production делает full history, dev — последние 7 дней. Параметр
start_date. - Tenant ID для multi-tenant приложения. Один и тот же проект, разные
tenant_idдля разных клиентов. - Бизнес-параметры.
vat_rate,default_currency,max_lookback_days. - Feature flags.
enable_experimental_columns: true— включает экспериментальные колонки в моделях.
Без vars вам пришлось бы:
- Заводить отдельный target для каждого варианта (плодит profiles.yml).
- Хардкодить значения и менять в каждой модели — не masштабируемо.
Vars решают это: один параметр, один источник истины, override на CLI.
Объявление в dbt_project.yml
# dbt_project.yml
name: 'jaffle_shop'
version: '1.0.0'
profile: 'jaffle_shop'
vars:
start_date: '2020-01-01'
end_date: '2030-12-31'
default_currency: 'USD'
lookback_days: 365
enable_pii_columns: false
Это глобальные vars. Каждая модель / macro может их прочитать через var('start_date').
Per-package vars
Если устанавливаете пакет (например, dbt_utils), его vars можно задать в namespace пакета:
vars:
# Глобальные
start_date: '2020-01-01'
# Per-package (dbt_utils)
dbt_utils:
surrogate_key_treat_nulls_as_empty_strings: true
surrogate_key_treat_nulls_as_empty_strings — конкретная опция dbt_utils. Через namespace пакета вы изолируете её от своих vars.
Использование в моделях
-- models/staging/stg_jaffle__orders.sql
select *
from {{ source('jaffle', 'raw_orders') }}
where order_date >= '{{ var("start_date") }}'
and order_date <= '{{ var("end_date") }}'
После compile:
select *
from "jaffle_shop"."main"."raw_orders"
where order_date >= '2020-01-01'
and order_date <= '2030-12-31'
Обратите внимание на кавычки: var('start_date') возвращает строку, и вы сами добавляете SQL-кавычки '...'. Без них компиляция даст where order_date >= 2020-01-01 — невалидный SQL.
var с default
В коде моделей часто хочется иметь fallback: «если var задана — использовать; если нет — использовать разумный default».
{{ var('lookback_days', 7) }}
Если lookback_days есть в dbt_project.yml или передано на CLI — возьмётся это значение. Если нет — 7.
where order_date >= current_date - interval '{{ var("lookback_days", 7) }}' day
Default — второй позиционный аргумент var(). Любой Jinja-объект: число, строка, список, dict.
Best practice junior: ВСЕГДА передавайте default к var(), кроме случая, когда значение реально обязательно. Без default var('x') для несуществующей переменной возвращает None — и SQL может тихо сломаться.
Override через CLI: —vars
Главная фишка vars — переопределение во время запуска, не меняя файлов:
$ dbt run --select stg_jaffle__orders --vars '{start_date: "2026-01-01", lookback_days: 30}'
Это вынудит модели читать start_date='2026-01-01' и lookback_days=30, даже если в dbt_project.yml другие значения. После завершения команды dbt — vars возвращаются к defaults.
YAML-синтаксис в --vars:
# Простые типы
$ dbt run --vars '{key1: value1, key2: 123}'
# Список
$ dbt run --vars '{categories: ["electronics", "apparel"]}'
# Вложенный объект
$ dbt run --vars '{tenant: {id: 42, name: "acme"}}'
# Многострочный (через bash heredoc)
$ dbt run --vars "$(cat <<EOF
{
start_date: '2026-01-01',
end_date: '2026-12-31',
categories: ['electronics', 'food']
}
EOF
)"
Иерархия источников var
Когда модель читает var('start_date'), dbt ищет значение по такой иерархии:
Этот порядок критичен: CLI всегда побеждает. Это значит, в CI/CD можно безопасно передавать прод-параметры через --vars, не правя файлов в репо.
Реальный паттерн: dev vs prod через target + vars
Допустим, в dbt_project.yml:
vars:
lookback_days: 365 # default — для prod
В моделях:
{% set days = var('lookback_days') %}
select *
from {{ ref('orders') }}
where order_date >= current_date - interval '{{ days }}' day
В prod-target — берётся 365 дней (default).
В dev-target — передаём через CLI:
$ dbt run --target dev --vars '{lookback_days: 7}'
Или прописываем в profiles.yml в самом target (см. урок 3).
Это минимально достаточный паттерн для dev vs prod, без сложной логики.
var в config()
var можно использовать не только в SQL, но и в config() блоке модели:
{{ config(
materialized=var('orders_materialization', 'table'),
cluster_by=var('orders_cluster_by', ['order_date'])
) }}
select * from ...
Это позволяет переключать materialization через --vars '{orders_materialization: view}'. Удобно для dev, где table-материализации замедляют итерации.
var в YAML-конфигурации
В _models.yml тоже можно:
models:
- name: stg_jaffle__orders
config:
enabled: "{{ var('include_staging_orders', true) }}"
enabled: false отключает модель — она не будет собрана и в DAG. Через --vars '{include_staging_orders: false}' модель можно временно выключить.
Получение всех vars: var without args
{% for k, v in var() %}
-- key: {{ k }}, value: {{ v }}
{% endfor %}
Чисто debug-сценарий. На junior уровне не используется, но полезно знать.
Лучшие практики
1. Всегда передавай default.
{# Bad #}
{{ var('start_date') }} -- может вернуть None
{# Good #}
{{ var('start_date', '2020-01-01') }}
2. Документируй vars в README или dbt_project.yml.
vars:
# start_date: дата отсечения для всех staging-моделей.
# В dev обычно current_date - 7 дней, в prod — историческая дата.
start_date: '2020-01-01'
# lookback_days: окно для incremental моделей.
# Меньше значения = быстрее, больше = надёжнее (поймать late-arrival).
lookback_days: 365
Через год коллеги (или вы) скажете спасибо.
3. Не злоупотребляй vars.
Vars — для параметров конфигурации. Если значение не меняется между запусками — это константа, и она должна быть либо хардкоднута, либо в seed-таблице.
Пример хорошего var: start_date, tenant_id, currency.
Пример плохого var: revenue_threshold = 1000 — не настройка, а бизнес-параметр. Лучше в seed business_constants с историей изменений.
4. Префикс для namespace по модулям.
В большом проекте полезно: vars: { fct: { default_lookback: 7 }, dim: { ... } }. Доступ — {{ var('fct').default_lookback }}. Снижает шанс случайно overwrite чужой vars.
5. Используй var в config(), не в WHERE-clause напрямую.
{# Bad: где-clause зависит от var, может неожиданно поменять плана выполнения #}
where order_date >= '{{ var("start_date") }}'
{# Better: вынеси в CTE с явной обработкой default #}
{% set start_date = var('start_date', '2020-01-01') %}
where order_date >= '{{ start_date }}'
Семантически одно и то же, но в {% set %} value явно зафиксирован, легче дебажить.
Распространённые ошибки
1. var без default -> None.
{# Если в dbt_project.yml нет start_date — компилируется в #}
where order_date >= 'None'
Странный SQL. DuckDB упадёт, Postgres вернёт пустой результат.
Лечение: всегда default.
2. Передача vars без кавычек.
$ dbt run --vars '{start_date: 2026-01-01}' # YAML распарсит '2026-01-01' как строку, но... опасно
$ dbt run --vars '{start_date: "2026-01-01"}' # Безопаснее: явные кавычки
YAML парсит 2026-01-01 как дату-объект, dbt передаёт его в Jinja, и при '{{ var("start_date") }}' получаем 2026-01-01 00:00:00 — формат может не совпасть с ожиданием.
Best practice: всегда кавычить строки в --vars JSON-style: --vars '{"start_date": "2026-01-01"}'.
3. Перепутать var и env_var.
var() — параметр проекта. env_var() — environment variable, для секретов и runtime-конфигурации. Об их различиях — следующий урок.
4. var в comment vs SQL.
{# var в Jinja comment — НЕ интерпретируется #}
{# {{ var('start_date') }} — это просто текст #}
{# var в SQL comment — интерпретируется #}
-- {{ var('start_date') }} — будет подставлен на этапе compile
Странность: SQL-комментарий пройдёт через Jinja, потому что Jinja парсит ВЕСЬ файл, не только SQL.
Попробуй сам
В dbt_project.yml добавьте:
vars:
default_country: 'US'
excluded_statuses: ['cancelled', 'fraud']
enable_pii: false
В модели используйте все три:
-- models/marts/marts__customer_summary.sql
select
customer_id,
case when country = '{{ var("default_country") }}' then 'home' else 'intl' end as customer_segment,
{% if var('enable_pii') %}
email,
phone,
{% endif %}
count(*) as order_count
from {{ ref('stg_jaffle__orders') }}
where status not in (
{% for s in var('excluded_statuses') %}
'{{ s }}'{% if not loop.last %},{% endif %}
{% endfor %}
)
group by 1, 2
{% if var('enable_pii') %}, email, phone{% endif %}
Запустите:
# Без PII (default)
$ dbt run --select marts__customer_summary
# С PII
$ dbt run --select marts__customer_summary --vars '{enable_pii: true}'
# Override country и статусы
$ dbt run --select marts__customer_summary --vars '{default_country: "DE", excluded_statuses: ["cancelled"]}'
Посмотрите в target/compiled/ — увидите, что SQL меняется в зависимости от vars.
Итоги
var('name')читает параметр проекта. Источник:vars:вdbt_project.ymlили CLI--vars.var('name', default)— fallback, если не задано. Best practice: всегда default.- Приоритет: CLI
--vars>dbt_project.yml> default в коде. - Per-package vars:
vars: { dbt_utils: { option: value } }— namespace для изоляции. - Используется в SQL, в
config(), в YAML-конфигах. - Типовой паттерн: dev vs prod через
var('lookback_days')с разными значениями на target. - Антипатерны: var без default, var для констант, передача без кавычек, путаница с env_var.
В следующем уроке — env_var(): чтение из environment variables, для секретов и runtime-конфигурации.