Templating

Merender template Jinja pada field operator untuk membangun path dan parameter dinamis.

Daftar isi

Sebagian besar pipeline membutuhkan nilai yang berubah setiap kali DAG dijalankan, misalnya folder partisi harian atau nama berkas laporan. Topik ini membahas templating, yakni penyisipan ekspresi pada parameter operator (field) menggunakan Jinja, yaitu library template Python. Dengan templating, nilai seperti itu dibentuk saat task berjalan, bukan saat berkas DAG ditulis. Pembahasan mencakup field mana yang mendukung template, kunci konteks yang paling sering digunakan, filter Jinja, serta cara memverifikasi hasil render melalui CLI. Setelah membacanya, perintah dan path yang dinamis dapat ditulis tanpa menyunting kode DAG setiap hari.

Field operator yang ditemplating#

Airflow merender template menggunakan Jinja, yang mengganti setiap ekspresi dalam tanda kurung kurawal ganda {{ ... }} dengan nilai dari konteks dag run. Penggantian itu hanya berlaku pada field yang mendaftar sebagai templated field, yaitu parameter operator yang dinyatakan pada atribut template_fields milik kelas operator. Field di luar daftar tersebut dikirim apa adanya, sehingga isi template_fields perlu diketahui sebelum menyisipkan ekspresi. Potongan berikut memeriksa dua operator yang paling umum digunakan.

python
from airflow.providers.standard.operators.bash import BashOperator
from airflow.providers.standard.operators.python import PythonOperator

print(BashOperator.template_fields)
print(PythonOperator.template_fields)
text Keluaran
('bash_command', 'env', 'cwd')
('templates_dict', 'op_args', 'op_kwargs')

BashOperator merender field bash_command, sedangkan PythonOperator merender op_kwargs beserta op_args dan templates_dict. Konsekuensinya, {{ ds }} yang disisipkan pada parameter seperti task_id tidak pernah dirender dan tetap terkirim sebagai teks literal. Daftar lengkap kunci konteks yang dapat dirujuk, mulai dari kunci waktu hingga kunci konfigurasi, dipublikasikan pada halaman Templates reference yang tautannya ada di bagian Referensi.

Merender template dengan Jinja#

Template adalah teks biasa yang memuat ekspresi, sedangkan konteks adalah kamus kunci dan nilai yang digunakan untuk mengganti ekspresi tersebut. Render menghasilkan string baru tanpa mengubah teks aslinya, sehingga mekanismenya dapat diamati dengan Jinja saja tanpa menjalankan Airflow. Contoh berikut merender satu baris perintah dengan konteks tiruan yang isinya meniru beberapa kunci konteks Airflow.

python
from jinja2 import Template

# konteks tiruan, yaitu kunci yang juga disiapkan Airflow saat task berjalan
konteks = {"ds": "2026-11-04", "ds_nodash": "20261104", "nama_berkas": "laporan.csv"}

template = Template(
    "arsipkan {{ nama_berkas }} dari {{ ds }} ke /data/{{ ds_nodash[:4] }}/"
    "{{ ds | replace(\"-\", \"\") }}"
)

print(template.render(**konteks))
text Keluaran
arsipkan laporan.csv dari 2026-11-04 ke /data/2026/20261104

Kunci ds memuat tanggal awal data interval dalam bentuk YYYY-MM-DD, sedangkan ds_nodash adalah versi tanpa tanda hubungnya. Ekspresi di dalam kurawal ganda tetap mengikuti aturan Python, sehingga ds_nodash[:4] mengambil empat karakter pertama sebagai tahun. Tanda pipa | memanggil filter, yaitu fungsi transformasi Jinja yang mengubah nilai di sebelah kiri tanda pipa, misalnya replace untuk mengganti teks. Filter bawaan lain seperti upper untuk huruf besar dan tojson untuk merender nilai menjadi JSON juga dapat digunakan pada field operator.

Kunci konteks tidak berhenti pada kunci tanggal sederhana. Kunci data_interval_start dan data_interval_end memuat batas data interval berupa objek pendulum.DateTime, sehingga atribut dan metode milik objek tanggal-waktu dapat dipanggil dari dalam template. Bentuk tanggal yang tidak tersedia sebagai kunci siap pakai dapat dibentuk langsung di dalam template, misalnya {{ data_interval_start.strftime("%Y%m%d") }} yang menghasilkan 20261104 untuk data interval yang dimulai pada 2026-11-04.

DAG dengan perintah bertemplate#

Berkas dag_templating.py berikut memuat dua task BashOperator dengan bash_command yang ditemplating. Kunci ds mengisi pesan pemrosesan partisi, sedangkan ds_nodash membentuk folder arsip. Jadwal @daily memberi data interval sepanjang satu hari, sehingga kedua kunci tersebut mengikuti tanggal data interval dag run.

python dag_templating.py
from airflow.providers.standard.operators.bash import BashOperator
from airflow.sdk import dag


@dag(dag_id="dag_templating", schedule="@daily")
def dag_templating():
    partisi = BashOperator(
        task_id="partisi",
        bash_command="echo \"memproses {{ ds }}\"",
    )
    arsip = BashOperator(
        task_id="arsip",
        bash_command="echo \"arsip ke /data/{{ ds_nodash }}\"",
    )

    # arsip baru berjalan setelah partisi selesai
    partisi >> arsip


dag_templating()

Seluruh langkah berikutnya dijalankan pada lingkungan latihan terpisah agar pemindaian folder dags tetap cepat.

bash
# lingkungan latihan terpisah agar DAG bawaan tidak ikut dipindai
export AIRFLOW_HOME=/tmp/ra-templating
export AIRFLOW__CORE__LOAD_EXAMPLES=False
bash
# uji bahwa berkas DAG dapat diparse tanpa keluaran apa pun
python3 dag_templating.py
bash
# salin berkas DAG ke folder yang dipindai Airflow
mkdir -p "$AIRFLOW_HOME/dags"
cp dag_templating.py "$AIRFLOW_HOME/dags/"
bash
# siapkan skema basis data metadata
# log migrasi berisi stempel waktu, sehingga tidak ditampilkan
airflow db migrate > /dev/null 2>&1

Melihat hasil render sebelum berjalan#

Hasil render dapat diperiksa tanpa menjalankan task melalui perintah airflow tasks render, yang menerima dag_id, task_id, dan nilai logical date. Tanggal 2026-11-04 pada argumen menentukan konteks yang dirender. Dengan jadwal @daily, data interval dimulai pada tanggal tersebut, sehingga ds bernilai 2026-11-04.

bash
# tampilkan field operator setelah template dirender
airflow tasks render dag_templating partisi 2026-11-04
text Keluaran
# ----------------------------------------------------------
# property: bash_command
# ----------------------------------------------------------
echo "memproses 2026-11-04"
# ----------------------------------------------------------
# property: env
# ----------------------------------------------------------
None
# ----------------------------------------------------------
# property: cwd
# ----------------------------------------------------------
None

Bagian bash_command memperlihatkan bahwa {{ ds }} sudah diganti dengan nilai 2026-11-04, sedangkan env dan cwd yang tidak ditemplating berisi nilai bawaan. Perintah ini menjadi alat periksa cepat apabila ada ekspresi template yang hasilnya mencurigakan.

Menjalankan DAG bertemplate#

Setelah hasil render sesuai harapan, seluruh DAG dijalankan sekali secara lokal dengan dags test.

bash
# jalankan seluruh task DAG sekali secara lokal
# log panjang dan bertanda waktu, sehingga diarahkan ke berkas
airflow dags test dag_templating 2026-11-04 > "$AIRFLOW_HOME/dags-test.log" 2>&1

Keluaran perintahnya panjang dan bertanda waktu, sehingga diarahkan ke berkas. Pada akhir log, kedua task berstatus success dan dag run ditutup dengan status yang sama. Bukti bahwa render terjadi sebelum perintah shell dieksekusi terdapat pada log task partisi. Log tersebut menampilkan perintah yang dijalankan dalam bentuk echo "memproses 2026-11-04", diikuti baris keluaran memproses 2026-11-04. Log task arsip memuat pola yang sama dengan folder /data/20261104 sebagai hasil render {{ ds_nodash }}, dan baris bukti kedua task diambil dari berkas log.

bash
# ambil bukti render dari berkas log kedua task
# opsi -m 1 membatasi keluaran pada kecocokan pertama
export AIRFLOW_HOME=/tmp/ra-templating
grep -m 1 -o 'echo "memproses 2026-11-04"' "$AIRFLOW_HOME/dags-test.log"
grep -m 1 -o "/data/20261104" "$AIRFLOW_HOME/dags-test.log"
text Keluaran
echo "memproses 2026-11-04"
/data/20261104

Apabila logical date diganti, misalnya menjadi 2026-11-05, nilai pada kedua log ikut berubah tanpa menyunting berkas DAG.

Kesalahan umum#

  • Menyisipkan ekspresi Jinja pada field yang tidak ditemplating, misalnya task_id. Field di luar template_fields tidak pernah dirender, sehingga teks {{ ds }} terkirim apa adanya dan nama task tampak rusak pada UI. Perbaikannya dengan memindahkan nilai dinamis ke field yang ditemplating seperti bash_command atau op_kwargs.
  • Menulis f-string Python sebagai pengganti ekspresi Jinja, misalnya bash_command=f"echo {ds}". Kunci konteks hanya ada di dalam konteks render dan bukan variabel Python pada berkas DAG, sehingga parser berhenti dengan pesan NameError saat berkas dimuat. Perbaikannya dengan membuang prefiks f dan menggunakan kurawal ganda.

Ringkasan#

  • Field yang ditemplating ditandai atribut template_fields: BashOperator merender bash_command, sedangkan PythonOperator merender templates_dict, op_args, dan op_kwargs.
  • Template dirender oleh Jinja menggunakan konteks dag run saat task dieksekusi, bukan saat berkas DAG diparse.
  • Kunci konteks waktu yang paling sering digunakan adalah ds, ds_nodash, dan data_interval_start, dan filter seperti replace serta tojson membentuk nilainya.
  • Perintah airflow tasks render menampilkan hasil render tanpa menjalankan task, sedangkan airflow dags test menjalankan DAG beserta lognya.

Referensi#