Jinja Templating in dbt

Free Lesson

Advertisement

Jinja Templating in dbt

Jinja Processing Architecture

Architecture Diagram
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    JINJA TEMPLATE PROCESSING FLOW                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                     β”‚
β”‚  β”‚   TEMPLATE  │───▢│   JINJA     │───▢│   RENDERED  β”‚                     β”‚
β”‚  β”‚   SOURCE    β”‚    β”‚   ENGINE    β”‚    β”‚   OUTPUT    β”‚                     β”‚
β”‚  β”‚             β”‚    β”‚             β”‚    β”‚             β”‚                     β”‚
β”‚  β”‚ β€’ SQL       β”‚    β”‚ β€’ Variables β”‚    β”‚ β€’ Pure SQL  β”‚                     β”‚
β”‚  β”‚ β€’ Jinja     β”‚    β”‚ β€’ Functions β”‚    β”‚ β€’ No Jinja  β”‚                     β”‚
β”‚  β”‚ β€’ Comments  β”‚    β”‚ β€’ Filters   β”‚    β”‚ β€’ Executableβ”‚                     β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β”‚
β”‚         β”‚                  β”‚                  β”‚                            β”‚
β”‚         β–Ό                  β–Ό                  β–Ό                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                     CONTEXT VARIABLES                               β”‚   β”‚
β”‚  β”‚  β€’ ref()        β€’ source()      β€’ var()        β€’ config()         β”‚   β”‚
β”‚  β”‚  β€’ this         β€’ schema        β€’ database     β€’ adapter           β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Macro Definition Flow

Architecture Diagram
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     MACRO DEFINITION AND USAGE                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                    MACRO DEFINITION                                 β”‚   β”‚
β”‚  β”‚                                                                     β”‚   β”‚
β”‚  β”‚  {% macro generate_schema_name(custom_schema_name, node) %}        β”‚   β”‚
β”‚  β”‚      {% set default_schema = target.schema %}                       β”‚   β”‚
β”‚  β”‚      {% if custom_schema_name %}                                    β”‚   β”‚
β”‚  β”‚          {% set return_value = custom_schema_name.strip() %}        β”‚   β”‚
β”‚  β”‚      {% else %}                                                     β”‚   β”‚
β”‚  β”‚          {% set return_value = default_schema %}                    β”‚   β”‚
β”‚  β”‚      {% endif %}                                                    β”‚   β”‚
β”‚  β”‚      {{ return(return_value) }}                                     β”‚   β”‚
β”‚  β”‚  {% endmacro %}                                                     β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                              β”‚                                              β”‚
β”‚                              β–Ό                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                    MACRO INVOCATION                                 β”‚   β”‚
β”‚  β”‚                                                                     β”‚   β”‚
β”‚  β”‚  {{ generate_schema_name('finance', node) }}                       β”‚   β”‚
β”‚  β”‚  Output: finance                                                    β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Block Structure

Architecture Diagram
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     JINJA BLOCK STRUCTURE                                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  {% block definition %}                                             β”‚   β”‚
β”‚  β”‚      {% set variables = [] %}                                       β”‚   β”‚
β”‚  β”‚      {% for item in items %}                                        β”‚   β”‚
β”‚  β”‚          {{ item }}                                                 β”‚   β”‚
β”‚  β”‚      {% endfor %}                                                   β”‚   β”‚
β”‚  β”‚      {% if condition %}                                             β”‚   β”‚
β”‚  β”‚          {{ output }}                                               β”‚   β”‚
β”‚  β”‚      {% endif %}                                                    β”‚   β”‚
β”‚  β”‚  {% endblock %}                                                     β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                             β”‚
β”‚  BLOCK TYPES:                                                               β”‚
β”‚  β€’ Variable Set:  {% set x = value %}                                      β”‚
β”‚  β€’ For Loop:      {% for i in items %}...{% endfor %}                      β”‚
β”‚  β€’ If/Else:       {% if condition %}...{% else %}...{% endif %}           β”‚
β”‚  β€’ Macro:         {% macro name() %}...{% endmacro %}                     β”‚
β”‚  β€’ Block:         {% block name %}...{% endblock %}                       β”‚
β”‚  β€’ Call:          {% call macro() %}...{% endcall %}                      β”‚
β”‚  β€’ Import:        {% import 'file' as alias %}                           β”‚
β”‚  β€’ From:          {% from 'file' import macro %}                         β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Detailed Explanation

Jinja2 is the templating engine that powers dbt's dynamic SQL generation capabilities. Understanding Jinja is essential for writing advanced dbt models, creating reusable macros, and building sophisticated data transformations.

Template Syntax

Jinja templates contain special delimiters that distinguish them from regular SQL:

  1. Variable Expressions: {{ variable }} - Output the value of a variable
  2. Statement Tags: {% statement %} - Execute Python-like code
  3. Comments: {# comment #} - Jinja comments that are stripped during rendering

Variables and Data Types

Jinja supports several data types:

  • Strings: Text values wrapped in quotes
  • Numbers: Integers and floats
  • Lists: Ordered collections using [item1, item2]
  • Dictionaries: Key-value pairs using {'key': 'value'}
  • Booleans: True or False

Variables can be set using the {% set %} tag:

{% set my_variable = 'value' %}
{% set my_list = [1, 2, 3] %}
{% set my_dict = {'key': 'value'} %}

Control Structures

Jinja provides several control structures for dynamic SQL generation:

For Loops: Iterate over collections

{% for column in columns %}
    {{ column }} as {{ column }}_renamed
    {% if not loop.last %},{% endif %}
{% endfor %}

Conditionals: Branch logic based on conditions

{% if incremental %}
    where updated_at > (select max(updated_at) from {{ this }})
{% endif %}

Macros: Reusable code blocks

{% macro empty_table(columns) %}
    select
        {% for col in columns %}
            cast(null as {{ col.data_type }}) as {{ col.name }}
            {% if not loop.last %},{% endif %}
        {% endfor %}
{% endmacro %}

Filters

Jinja filters transform values before output:

  • {{ value | upper }} - Convert to uppercase
  • {{ value | lower }} - Convert to lowercase
  • {{ value | trim }} - Remove whitespace
  • {{ value | join(', ') }} - Join list with separator
  • {{ value | default('N/A') }} - Provide default value
  • {{ value | length }} - Get length of string/list
  • {{ value | replace('old', 'new') }} - String replacement

Context Variables

dbt provides several built-in context variables:

  • {{ ref('model_name') }} - Reference another model
  • {{ source('source_name', 'table_name') }} - Reference a source
  • {{ var('variable_name') }} - Access project variables
  • {{ config(key='value') }} - Configure model settings
  • {{ this }} - Reference the current model
  • {{ target }} - Access target connection details
  • {{ adapter }} - Access adapter-specific functions

Code Examples

Dynamic Column Selection

-- models/staging/stg_orders.sql
{% set columns = ['id', 'customer_id', 'status', 'amount', 'created_at'] %}

with source as (
    select * from {{ source('raw', 'orders') }}
),

renamed as (
    select
        {% for col in columns %}
            {{ col }}
            {% if not loop.last %},{% endif %}
        {% endfor %}
    from source
)

select * from renamed

Conditional Column Generation

-- models/marts/fct_orders.sql
{% set include_profit = var('include_profit', false) %}

with orders as (
    select
        order_id,
        customer_id,
        order_date,
        amount,
        cost,
        {% if include_profit %}
            amount - cost as profit,
            safe_divide(amount - cost, amount) as profit_margin
        {% endif %}
    from {{ ref('stg_orders') }}
)

select * from orders

Reusable Macro for Date Spine

-- macros/date_spine.sql
{% macro date_spine(start_date, end_date, datepart="day") %}

    {% set n = dbt_utils.datediff(start_date, end_date, datepart) %}

    {{ dbt_utils.generate_series(n) }}

    select
        {{ dbt_utils.dateadd(datepart, "row_number() over (order by 1) - 1", start_date) }} as date_{{ datepart }}
    from {{ ref('dbt_utils_generate_series') }}
    where row_number() <= {{ n + 1 }}

{% endmacro %}

Advanced Macro with Dispatch

-- macros/cross_db_utils.sql
{% macro safe_cast(column, type) %}
    {% set macro = adapter.dispatch('safe_cast', 'dbt_utils')(column, type) %}
    {{ return(macro) }}
{% endmacro %}

{% macro default__safe_cast(column, type) %}
    safe_cast({{ column }} as {{ type }})
{% endmacro %}

{% macro postgres__safe_cast(column, type) %}
    cast({{ column }} as {{ type }})
{% endmacro %}

Dynamic Incremental Strategy

-- models/marts/fct_events.sql
{{
    config(
        materialized='incremental',
        incremental_strategy=var('incremental_strategy', 'merge'),
        unique_key='event_id',
        partition_by={
            "field": "event_date",
            "data_type": "date",
            "granularity": var('partition_granularity', 'day')
        },
        cluster_by=var('cluster_columns', ['user_id', 'event_type'])
    )
}}

with events as (
    select * from {{ ref('stg_events') }}
),

final as (
    select
        event_id,
        user_id,
        event_type,
        event_timestamp,
        cast(event_timestamp as date) as event_date,
        event_properties,
        current_timestamp() as dbt_updated_at
    from events
)

select * from final

{% if is_incremental() %}
where dbt_updated_at > (select max(dbt_updated_at) from {{ this }})
{% endif %}

Performance Metrics

OperationComplexityUse CaseExample
Variable SetO(1)Store values{% set x = 1 %}
For LoopO(n)Iterate lists{% for i in items %}
ConditionalO(1)Branch logic{% if condition %}
Macro CallO(1)Reusable code{{ macro() }}
FilterO(1)Transform values{{ x | upper }}
IncludeO(1)Import templates{{ include 'file' }}

Best Practices

  1. Use descriptive variable names for clarity and maintainability
  2. Keep macros small and focused on single responsibilities
  3. Use filters to transform values rather than inline logic
  4. Document macros with docstrings and comments
  5. Use {% set %} for complex expressions to improve readability
  6. Avoid deep nesting in control structures
  7. Use default() filter to provide fallback values
  8. Test macros with different input combinations

Advertisement

Need Expert dbt Help?

Get personalized tutoring, project support, or professional consulting.

Advertisement