Idenya
Toko dengan 15 produk tidak seharusnya butuh 15 route. Sebagai gantinya, satu route menangani pola /products/<id>, dan bagian di dalam kurung sudut masuk ke fungsimu sebagai argumen. Begitulah cara kerja setiap situs sungguhan: /products/3, /customers/ayu-lestari, /orders/2024/06.
Variabel di dalam path
Toko Kita punya banyak produk, dan masing-masing butuh halamannya sendiri: /products/1, /products/2, dan seterusnya. Menulis route terpisah untuk setiap produk itu konyol. Sebagai gantinya, kamu menulis satu route dengan penanda tempat untuk id-nya, dan Flask mengoper apa pun yang ada di posisi itu ke function-mu:
from flask import Flask
app = Flask(__name__)
PRODUCTS = {
1: {"name": "French Press 600ml", "price": 185000},
2: {"name": "Pour-over Kettle", "price": 320000},
3: {"name": "Arabica Gayo 250g", "price": 95000},
}
@app.route("/products/<int:product_id>")
def product(product_id):
item = PRODUCTS.get(product_id)
if item is None:
return f"No product {product_id}", 404
return f"<h1>{item['name']}</h1><p>Rp {item['price']:,}</p>"
client = app.test_client()
print(client.get("/products/2").get_data(as_text=True))
print(client.get("/products/99").status_code)
print(client.get("/products/abc").status_code)
<h1>Pour-over Kettle</h1><p>Rp 320,000</p>
404
404
<int:product_id>mencocokkan bilangan bulat di posisi itu dan meneruskannya sebagaiproduct_id, sudah dikonversi menjadiint.- Id yang tidak dikenal mengembalikan 404 dengan pesan, bukan crash.
/products/abcsama sekali tidak cocok dengan<int:...>, jadi Flask mengembalikan 404 bahkan sebelum fungsimu berjalan.
Converter
| Converter | Mencocokkan | Contoh |
|---|---|---|
<name> atau <string:name> | teks apa pun tanpa / (default) | /customers/ayu |
<int:name> | bilangan bulat | /orders/41 |
<float:name> | bilangan desimal | /discount/0.15 |
<path:name> | teks termasuk / | /files/reports/2024/june.csv |
Converter adalah validasi gratis: dengan <int:...>, fungsimu tidak akan pernah menerima "abc".
Beberapa parameter
Sebuah URL bisa membawa lebih dari satu nilai. Laporan bulanan butuh tahun dan bulan, seperti /reports/2024/7, dan setiap penanda tempat menjadi argumennya sendiri:
from flask import Flask
app = Flask(__name__)
@app.route("/reports/<int:year>/<int:month>")
def monthly_report(year, month):
if not 1 <= month <= 12:
return "Month must be 1 to 12", 404
return f"Report for {year}-{month:02d}"
client = app.test_client()
print(client.get("/reports/2024/6").get_data(as_text=True))
print(client.get("/reports/2024/13").status_code)
Report for 2024-06
404
Nama parameter di route harus sama dengan nama parameter fungsinya.
abort: berhenti dengan status error
Mengembalikan "...", 404 memang berfungsi, tetapi abort(404) lebih enak dibaca dan langsung menghentikan fungsi, di mana pun kamu berada:
from flask import Flask, abort
app = Flask(__name__)
CUSTOMERS = {"ayu-lestari": "Ayu Lestari", "budi-santoso": "Budi Santoso"}
@app.route("/customers/<slug>")
def customer(slug):
name = CUSTOMERS.get(slug) or abort(404)
return f"<h1>{name}</h1>"
client = app.test_client()
print(client.get("/customers/ayu-lestari").get_data(as_text=True))
print(client.get("/customers/nobody").status_code)
<h1>Ayu Lestari</h1>
404
Teks di URL (ayu-lestari) disebut slug: versi nama yang mudah dibaca dan aman untuk URL. Slug membuat URL lebih enak dilihat daripada id, tetapi harus unik.
url_for: jangan pernah hard-code URL
Link antarhalaman sebaiknya dibuat dengan url_for, dari nama fungsi dan parameternya:
from flask import Flask, url_for
app = Flask(__name__)
@app.route("/products/<int:product_id>")
def product(product_id):
return f"Product {product_id}"
@app.route("/")
def home():
link = url_for("product", product_id=3)
return f'<a href="{link}">Our best coffee</a>'
client = app.test_client()
print(client.get("/").get_data(as_text=True))
<a href="/products/3">Our best coffee</a>
Kalau nanti kamu mengubah route-nya menjadi /shop/items/<int:product_id>, setiap link url_for("product", ...) ikut berubah otomatis. String "/products/3" yang di-hard-code akan rusak semuanya.
Route mendeskripsikan pola URL; view function menerima bagian yang berubah-ubah sebagai argumen bertipe; url_for membuat URL dari nama fungsi. Kode Python-mu berbicara dalam fungsi dan id, tidak pernah dalam string URL yang dirakit manual.
Garis miring di akhir
@app.route("/products/") (dengan garis miring) juga menjawab /products dengan me-redirect ke versi bergaris miring. @app.route("/about") (tanpa garis miring) mengembalikan 404 untuk /about/. Pilih satu gaya dan konsisten: koleksi yang mirip folder dengan garis miring, halaman tunggal tanpa garis miring.
Mengapa ini penting untuk kode yang bersih
Dynamic route menjaga satu fungsi per jenis halaman, bukan per halaman, yaitu prinsip "don't repeat yourself" di level URL. Converter memvalidasi input sebelum kodemu berjalan, abort menjaga alur utama tetap datar, dan url_for menghilangkan seluruh golongan bug link rusak.
Rangkuman
@app.route("/products/<int:product_id>")meneruskan bagian URL sebagai argumen bertipe.- Converter:
string(default),int,float,path. - Kembalikan
..., 404atau panggilabort(404)untuk item yang tidak dikenal. - Buat link dengan
url_for("function_name", param=value), bukan string yang ditulis manual.