Learning Platform
Глоссарий Troubleshooting
Урок 13.01 · 18 мин
Начальный
dbtJinjavarconfiguration

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.

TIP

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 ищет значение по такой иерархии:

1. CLI: --vars '{start_date: X}'Самый высокий приоритет — переопределяет всё остальное
2. dbt_project.yml: vars: start_dateГлобальные параметры проекта
3. Default в var('start_date', default)Fallback, если ни CLI, ни project не определили
4. Если default не передан -> Nonevar() возвращает None, что часто = тихий bug в SQL

Этот порядок критичен: 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.

Проверка знанийKnowledge check
В чём разница между vars в dbt_project.yml и --vars на CLI? Какой источник побеждает при конфликте?
ОтветAnswer
vars в dbt_project.yml — стабильный, версионированный источник параметров. Хранится в git, виден всей команде. Подходит для дефолтных значений: что должно работать без явного override. --vars на CLI — runtime override. Не сохраняется в git, применяется только к текущей команде dbt. Подходит для: - Ad-hoc запусков с экспериментальными значениями. - CI/CD: production-параметры передаются скриптом, не правя файл. - Backfill: --vars '{start_date: 2020-01-01}' для одного запуска. Приоритет при конфликте: --vars ПОБЕЖДАЕТ. То есть если в dbt_project.yml: vars: { start_date: '2026-01-01' }, а на CLI dbt run --vars '{start_date: "2020-01-01"}' — модели увидят '2020-01-01'. Иерархия (по убыванию приоритета): 1. CLI --vars 2. vars: в dbt_project.yml 3. Default из var('name', default) в коде 4. None (если ни 1-3 не задано) Это позволяет иметь sensible defaults в файле и переопределять для специальных случаев на CLI. Best practice: в production CI/CD ВСЕ значимые vars передаются через --vars, dbt_project.yml содержит только dev/local defaults.
Проверка знанийKnowledge check
Что произойдёт с моделью, если var('start_date') читается без default, а в dbt_project.yml и CLI значения нет? Как защититься от такого тихого бага?
ОтветAnswer
Без default var('start_date') вернёт None. В Jinja None при подстановке в строку преобразуется в 'None' (литеральная строка). В SQL это даст что-то вроде: where order_date не меньше 'None' На разных warehouses реакция разная: - DuckDB: ошибка типа conversion ('None' не парсится как date). - PostgreSQL: ошибка type cast. - BigQuery: ошибка cast. - Иногда хуже — empty result или unexpected behaviour, если type coercion 'умная'. Главная проблема: ошибка не на parse phase, а на execute, и сообщение может быть запутанным. Защита: 1. ВСЕГДА default: var('start_date', '2020-01-01'). Это спасает от 95% случаев тихих багов. 2. Если значение реально обязательно (нет разумного default) — явная проверка: ''{% if not var('start_date') %}'' ''{{ exceptions.raise_compiler_error('start_date var is required for this model') }}'' ''{% endif %}'' exceptions.raise_compiler_error падает на parse, с понятным сообщением. CI красный, разработчик сразу видит. 3. Документируй обязательные vars в README с примером CLI-вызова. Команда новичков должна знать, что без --vars '{start_date: X}' проект не работает.

Итоги

  • 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.
target.name: conditional logic per environment

В следующем уроке — env_var(): чтение из environment variables, для секретов и runtime-конфигурации.

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 7. Какое значение получит var('start_date'), если в dbt_project.yml есть vars: { start_date: '2026-01-01' }, а на CLI передано --vars '{start_date: "2020-01-01"}'?

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

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

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

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