Jinja и макросы
Связанные справочные документы
- Документация по Jinja Template Designer (внешняя ссылка)
- Контекст Jinja в dbt
- Свойства макросов
Обзор
В dbt вы можете комбинировать SQL с Jinja, языком шаблонов.
Использование Jinja превращает ваш проект dbt в программную среду для SQL, предоставляя вам возможность делать то, что обычно невозможно в SQL. Важно отметить, что Jinja сам по себе не является языком программирования; вместо этого он выступает как инструмент для расширения возможностей SQL в ваших проектах dbt.
Например, с помощью Jinja вы можете:
- Использовать управляющие конструкции (например, операторы
ifи циклыfor) в SQL - Использовать переменные окружения в вашем dbt‑проекте для production‑развертываний
- Менять способ сборки проекта в зависимости от текущего target
- Работать с результатами одного запроса для генерации другого запроса, например:
- Возвращать список способов оплаты, чтобы создать столбец с подытогами для каждого способа оплаты (pivot)
- Возвращать список столбцов из двух relations и выбирать их в одном и том же порядке, чтобы упростить их объединение через union
- Выносить фрагменты SQL в переиспользуемые macros — они аналогичны функциям в большинстве языков программирования
Если вы использовали функцию {{ ref() }}, вы уже используете Jinja!
Jinja можно использовать в любом SQL-коде внутри проекта dbt, включая models, analyses, data tests и даже hooks.
Посмотрите руководство по использованию Jinja для пошагового примера использования Jinja в модели и превращения его в макрос!
Начало работы
Jinja
Вот пример модели dbt, использующей Jinja:
{% set payment_methods = ["bank_transfer", "credit_card", "gift_card"] %}
select
order_id,
{% for payment_method in payment_methods %}
sum(case when payment_method = '{{payment_method}}' then amount end) as {{payment_method}}_amount,
{% endfor %}
sum(amount) as total_amount
from app_data.payments
group by 1
Этот запрос будет скомпилирован в:
select
order_id,
sum(case when payment_method = 'bank_transfer' then amount end) as bank_transfer_amount,
sum(case when payment_method = 'credit_card' then amount end) as credit_card_amount,
sum(case when payment_method = 'gift_card' then amount end) as gift_card_amount,
sum(amount) as total_amount
from app_data.payments
group by 1
Вы можете распознать Jinja по разделителям, которые использует язык, которые мы называем "фигурными скобками":
- Выражения
{{ ... }}: Выражения используются, когда вы хотите вывести строку. Вы можете использовать выражения для ссылки на переменные и вызова макросов. - Операторы
{% ... %}: Операторы не выводят строку. Они используются для управления потоком, например, для настройки цикловforи операторовif, для установки или изменения переменных или для определения макросов. - Комментарии
{# ... #}: Комментарии Jinja используются для предотвращения выполнения текста внутри комментария или вывода строки. Не используйте--для комментариев.
При использовании Jinja в модели dbt она должна компилироваться в корректный SQL-запрос. Чтобы проверить, в какой SQL компилируется ваша Jinja:
- При использовании dbt: нажмите кнопку compile, чтобы увидеть скомпилированный SQL в панели Compiled SQL
- При использовании dbt Core: выполните команду
dbt compileв командной строке. Затем откройте файл со скомпилированным SQL в директорииtarget/compiled/{project name}/. Используйте режим разделённого экрана в вашем редакторе кода, чтобы держать оба файла открытыми одновременно.
Макросы
Макросы в Jinja — это фрагменты кода, которые можно использовать многократно — они аналогичны "функциям" в других языках программирования и чрезвычайно полезны, если вы обнаруживаете, что повторяете код в нескольких моделях. Макросы определяются в .sql файлах, обычно в вашем каталоге macros (документация).
Файлы макросов могут содержать один или несколько макросов — вот пример:
{% macro cents_to_dollars(column_name, scale=2) %}
({{ column_name }} / 100)::numeric(16, {{ scale }})
{% endmacro %}
Модель, использующая этот макрос, может выглядеть так:
select
id as payment_id,
{{ cents_to_dollars('amount') }} as amount_usd,
...
from app_data.payments
Это будет скомпилировано в:
select
id as payment_id,
(amount / 100)::numeric(16, 2) as amount_usd,
...
from app_data.payments
💡 Используйте управление пробелами Jinja, чтобы упорядочить ваши макросы!
Когда вы изменяете макросы в вашем проекте, вы можете заметить лишние пробелы в вашем коде в папке target/compiled.
Вы можете удалить ненужные пробелы и строки с помощью управления пробелами Jinja, используя знак минус. Например, используйте {{- ... -}} или {%- ... %} вокруг определений ваших макросов (таких как {%- macro generate_schema_name(...) -%} ... {%- endmacro -%}).
Использование макроса из пакета
Ряд полезных макросов также был сгруппирован в пакеты — наш самый популярный пакет — dbt-utils.
После установки пакета в ваш проект, вы можете использовать любой из макросов в вашем собственном проекте — убедитесь, что вы квалифицируете макрос, добавляя префикс с именем пакета:
select
field_1,
field_2,
field_3,
field_4,
field_5,
count(*)
from my_table
{{ dbt_utils.dimensions(5) }}
Вы также можете квалифицировать макрос в вашем собственном проекте, добавляя префикс с вашим именем пакета (это в основном полезно для авторов пакетов).
Часто задаваемые вопросы
dbtonic Jinja
Как хорошо написанный код на Python называют "pythonic", так и хорошо написанный код dbt называют "dbtonic".
Предпочитайте читаемость вместо DRY-ности
Как только вы узнаете о возможностях Jinja, часто возникает желание абстрагировать каждую повторяющуюся строку в макрос! Помните, что использование Jinja может сделать ваши модели трудными для интерпретации другими пользователями — мы рекомендуем отдавать предпочтение читаемости при смешивании Jinja с SQL, даже если это означает повторение некоторых строк SQL в нескольких местах. Если все ваши модели — это макросы, возможно, стоит пересмотреть подход.
Используйте макросы из пакетов
Пишете макрос в первый раз? Проверьте, не открыли ли мы исходный код в dbt-utils, который вы можете использовать, и сэкономьте себе время!
Устанавливайте переменные в начале модели
{% set ... %} может использоваться для создания новой переменной или обновления существующей. Мы рекомендуем устанавливать переменные в начале модели, а не жестко кодировать их в строке. Это практика, заимствованная из многих других языков программирования, так как она помогает с читаемостью и пригодится, если вам нужно будет ссылаться на переменную в двух местах:
-- 🙅 Это работает, но может быть трудно поддерживать по мере роста вашего кода
{% for payment_method in ["bank_transfer", "credit_card", "gift_card"] %}
...
{% endfor %}
-- ✅ Это наш предпочтительный метод установки переменных
{% set payment_methods = ["bank_transfer", "credit_card", "gift_card"] %}
{% for payment_method in payment_methods %}
...
{% endfor %}