Flask Form: Input, Validasi, dan Flash Message
Begitu aplikasi menerima form, data tidak lagi sepenuhnya berada di bawah kendali kita. Field bisa kosong, hanya berisi spasi, hilang dari request, atau diisi jauh lebih panjang dari yang diperkirakan. Karena itu, backend tetap perlu memvalidasi input meskipun HTML sudah memakai atribut seperti required.
Contoh berikut membuat form tambah task dengan alur yang umum dipakai: halaman dibuka lewat GET, data dikirim lewat POST, lalu server melakukan redirect setelah selesai memprosesnya.
Memahami Alur GET, POST, lalu Redirect
Saat user membuka /tasks/new, browser mengirim request GET dan Flask menampilkan form. Setelah tombol Save ditekan, browser mengirim POST /tasks beserta data input. Route create memeriksa data itu, menyimpannya jika valid, lalu mengarahkan browser kembali ke halaman form.
Urutan ini sering disebut pola Post/Redirect/Get. Redirect membuat request terakhir di browser kembali menjadi GET. Hasilnya, menekan refresh tidak mengirim ulang POST dan tidak membuat task yang sama dua kali.
Menangani Form di Flask
Untuk sementara, data disimpan di list agar perhatian kita tetap pada alur form. Database bisa menggantikan list ini nanti tanpa mengubah konsep validasinya.
# File: app.py
from flask import Flask, flash, redirect, render_template, request, url_for
app = Flask(__name__)
app.secret_key = "dev-secret"
tasks = []
@app.get("/tasks/new")
def new_task():
return render_template("new_task.html")
@app.post("/tasks")
def create_task():
title = request.form.get("title", "").strip()
if not title:
flash("Judul task wajib diisi.", "error")
return redirect(url_for("new_task"))
if len(title) > 100:
flash("Judul task maksimal 100 karakter.", "error")
return redirect(url_for("new_task"))
tasks.append(title)
flash("Task berhasil ditambahkan.", "success")
return redirect(url_for("new_task"))
request.form.get("title", "") lebih fleksibel daripada request.form["title"]. Kalau field title tidak ikut terkirim, .get() memberi string kosong sehingga aplikasi masih bisa menampilkan pesan validasi alih-alih berhenti dengan error BadRequestKeyError.
Method .strip() membuang spasi di awal dan akhir. Tanpa strip, input berisi beberapa spasi akan lolos dari pengecekan if not title padahal isinya hanya spasi.
flash() menyimpan pesan untuk request berikutnya. Flask membutuhkan secret_key karena flash message menggunakan session. Nilai "dev-secret" hanya cocok untuk latihan di environment lokal; di production, ambil secret yang panjang dan acak dari environment variable.
Tampilkan Form dan Pesannya
Template perlu menampilkan flash message secara eksplisit. Kalau hanya memanggil flash() di route tanpa membaca pesannya di template, user tidak akan melihat feedback apa pun.
<!-- File: templates/new_task.html -->
<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>Tambah Task</title>
</head>
<body>
{% with messages = get_flashed_messages(with_categories=true) %}
{% for category, message in messages %}
<p class="message {{ category }}">{{ message }}</p>
{% endfor %}
{% endwith %}
<h1>Tambah Task</h1>
<form method="post" action="{{ url_for('create_task') }}">
<label for="title">Judul</label>
<input id="title" name="title" maxlength="100" required>
<button type="submit">Save</button>
</form>
</body>
</html>
action="{{ url_for('create_task') }}" menghasilkan URL berdasarkan nama function route. Cara ini lebih tahan perubahan daripada menulis /tasks di banyak template. Kalau path route berubah, template tetap mengarah ke endpoint yang benar.
Kategori error dan success ikut disimpan bersama pesan. Nantinya kategori tersebut bisa dipakai sebagai class CSS agar feedback gagal dan berhasil mudah dibedakan secara visual.
Validasi Browser Bukan Pengganti Validasi Server
Atribut required dan maxlength membantu user mengisi form dengan benar, tetapi keduanya berjalan di browser dan bisa dilewati. Request juga dapat dikirim lewat curl, script, atau tool API tanpa melewati form HTML. Pengecekan di route tetap menjadi sumber kebenaran.
Untuk form yang menyimpan data penting, tambahkan perlindungan CSRF. Salah satu pilihan umum di Flask adalah Flask-WTF. Contoh ini belum memakai Flask-WTF agar alur request dan validasi dasar tetap mudah terlihat, jadi jangan langsung menyalinnya ke form production yang mengubah data sensitif.
Coba Beberapa Input yang Berbeda
- Submit tanpa mengisi judul dan pastikan pesan error muncul.
- Kirim judul yang hanya berisi spasi.
- Coba masukkan lebih dari 100 karakter.
- Tambahkan field
descriptionyang boleh kosong. - Setelah submit sukses, ubah redirect agar menuju halaman daftar task.
Saat latihan tersebut berjalan, perhatikan bahwa form yang baik tidak hanya menerima input. Ia juga menjelaskan apa yang salah, mempertahankan alur browser yang aman saat di-refresh, dan tidak pernah berasumsi bahwa data dari client pasti benar.
Artikel Terkait
Lanjutkan membaca topik yang masih satu konteks.
Flask Authentication: Login, Session, dan Logout
Buat authentication Flask dengan password hash, login session, current user, protected route, dan logout tanpa menyimpan password mentah.
Flask CRUD SQLAlchemy: Create, Read, Update, Delete
Bangun CRUD Flask dan SQLAlchemy untuk membuat, membaca, mengubah, serta menghapus note lengkap dengan validasi, form, dan redirect.
Flask SQLAlchemy: Koneksi Database dan Model
Hubungkan Flask ke SQLite dengan Flask-SQLAlchemy, buat model dan tabel, lalu pahami proses add, commit, serta query data.
Struktur Project Flask: Blueprint, Template, dan Config
Rapikan struktur project Flask dengan application factory, Blueprint, folder route, template, static files, dan config yang terpisah.