Python

Flask: Micro Web Framework Python

Tutorial lengkap belajar Flask dari nol β€” routing, templates, forms, database, REST API, deployment, dan quiz interaktif dengan contoh kode praktis

1. Pengenalan Flask

Flask adalah micro web framework Python yang dikembangkan oleh Armin Ronacher dan pertama kali dirilis pada tahun 2010. Disebut "micro" karena Flask memiliki inti yang minimalis β€” hanya menyediakan routing, request handling, dan templating. Fitur lain seperti database, autentikasi, dan form validation ditambahkan melalui extension.

Flask sangat cocok untuk proyek kecil-menengah, API, prototype, dan bagi developer yang menginginkan fleksibilitas penuh dalam memilih komponen yang digunakan.

Flask vs Django

Aspek Flask Django
TipeMicro frameworkFull-stack framework
FilosofiMinimalis, fleksibelBatteries included
DatabaseFlask-SQLAlchemy (extension)Django ORM (built-in)
Admin PanelFlask-Admin (extension)Built-in
TemplateJinja2Django Template Language
URL RoutingDecorator-basedurls.py configuration
Cocok untukAPI, Microservice, PrototypeWeb app besar, CMS
Learning Curve🟒 Mudah🟑 Sedang
Diagram: Arsitektur Flask
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 FLASK APPLICATION                     β”‚
β”‚                                                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                  Flask Core                     β”‚  β”‚
β”‚  β”‚  β€’ Werkzeug (WSGI toolkit)                     β”‚  β”‚
β”‚  β”‚  β€’ Jinja2 (Template engine)                    β”‚  β”‚
β”‚  β”‚  β€’ URL Routing                                 β”‚  β”‚
β”‚  β”‚  β€’ Request/Response handling                    β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚ Extensionβ”‚  β”‚ Extensionβ”‚  β”‚    Extension      β”‚    β”‚
β”‚  β”‚ Flask-   β”‚  β”‚ Flask-   β”‚  β”‚    Flask-         β”‚    β”‚
β”‚  β”‚ SQLAlchemyβ”‚  β”‚  Login   β”‚  β”‚    WTF (Forms)    β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚ Extensionβ”‚  β”‚ Extensionβ”‚  β”‚    Extension      β”‚    β”‚
β”‚  β”‚ Flask-   β”‚  β”‚ Flask-   β”‚  β”‚    Flask-         β”‚    β”‚
β”‚  β”‚  Admin   β”‚  β”‚  Mail    β”‚  β”‚    Migrate         β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Instalasi dan Hello World

Setup Virtual Environment

Bash
# Buat folder proyek
mkdir belajar-flask && cd belajar-flask

# Buat virtual environment
python -m venv venv
source venv/bin/activate  # macOS/Linux
# venv\Scripts\activate   # Windows

# Instal Flask
pip install flask

# Verifikasi
python -c "import flask; print(flask.__version__)"
# Output: 3.0.3

Flask App Pertama

Python β€” app.py
# app.py β€” Aplikasi Flask pertama
from flask import Flask

# Buat instance Flask
app = Flask(__name__)

# Route utama
@app.route('/')
def beranda():
    return '<h1>Halo, selamat datang di Flask!</h1>'

# Route dengan parameter
@app.route('/sapa/<nama>')
def sapa(nama):
    return f'<h1>Halo, {nama}! Selamat datang!</h1>'

# Jalankan development server
if __name__ == '__main__':
    app.run(debug=True, port=5000)

# Jalankan dari terminal:
# python app.py
# Output:
#  * Running on http://127.0.0.1:5000
#  * Debug mode: on

Struktur Proyek Flask

File Structure
belajar-flask/
β”œβ”€β”€ venv/                  ← Virtual environment
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ __init__.py        ← Flask app factory
β”‚   β”œβ”€β”€ routes.py          ← URL routes
β”‚   β”œβ”€β”€ models.py          ← Database models
β”‚   β”œβ”€β”€ forms.py           ← Form definitions
β”‚   β”œβ”€β”€ config.py          ← Konfigurasi
β”‚   β”œβ”€β”€ static/            ← CSS, JS, images
β”‚   β”‚   β”œβ”€β”€ css/
β”‚   β”‚   β”œβ”€β”€ js/
β”‚   β”‚   └── images/
β”‚   └── templates/         ← HTML templates
β”‚       β”œβ”€β”€ base.html
β”‚       β”œβ”€β”€ beranda.html
β”‚       └── tentang.html
β”œβ”€β”€ tests/                 ← Unit tests
β”œβ”€β”€ requirements.txt       ← Dependencies
β”œβ”€β”€ .env                   ← Environment variables
└── run.py                 ← Entry point

App Factory Pattern

Python β€” app/__init__.py
# app/__init__.py β€” App Factory Pattern
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

db = SQLAlchemy()
migrate = Migrate()

def create_app(config_name='development'):
    app = Flask(__name__)

    # Load konfigurasi
    app.config.from_object(f'app.config.{config_name.capitalize()}Config')

    # Inisialisasi extensions
    db.init_app(app)
    migrate.init_app(app, db)

    # Register blueprints
    from app.routes import main_bp
    app.register_blueprint(main_bp)

    from app.api import api_bp
    app.register_blueprint(api_bp, url_prefix='/api')

    return app
πŸ’‘ Tips

Gunakan App Factory Pattern (create_app()) untuk proyek yang lebih besar. Pola ini memudahkan testing, konfigurasi multiple environments, dan organisasi kode yang lebih baik.

3. Routing dan URL

Routing di Flask menggunakan decorator @app.route() yang memetakan URL ke fungsi Python. Flask mendukung URL variables, HTTP methods, dan URL building.

Python β€” routes.py
from flask import Blueprint, request, redirect, url_for, abort, jsonify

main_bp = Blueprint('main', __name__)

# Route dasar
@main_bp.route('/')
def beranda():
    return 'Halaman Beranda'

# Route dengan HTTP methods
@main_bp.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        # Proses login...
        return f'Login berhasil: {username}'
    return 'Halaman Login'

# URL variables β€” tipe data
@main_bp.route('/artikel/<int:id>')
def artikel_by_id(id):
    return f'Artikel ID: {id}'

@main_bp.route('/user/<username>')
def profil(username):
    return f'Profil: {username}'

@main_bp.route('/file/<path:filepath>')
def file_path(filepath):
    return f'File: {filepath}'

# Multiple URL untuk satu fungsi
@main_bp.route('/beranda')
@main_bp.route('/home')
def home():
    return redirect(url_for('main.beranda'))

# URL building β€” generate URL dari fungsi
@main_bp.route('/link-demo')
def link_demo():
    url1 = url_for('main.beranda')                    # /
    url2 = url_for('main.artikel_by_id', id=5)        # /artikel/5
    url3 = url_for('main.profil', username='budi')    # /user/budi
    return f'URL1: {url1}, URL2: {url2}, URL3: {url3}'

# Error handlers
@main_bp.errorhandler(404)
def not_found(error):
    return '<h1>404 β€” Halaman Tidak Ditemukan</h1>', 404

@main_bp.errorhandler(500)
def server_error(error):
    return '<h1>500 β€” Server Error</h1>', 500

# Blueprint dengan sub-bp
from flask import Blueprint
api_bp = Blueprint('api', __name__)

@api_bp.route('/artikel')
def get_artikel():
    return jsonify({'artikel': []})

4. Templates dengan Jinja2

Flask menggunakan Jinja2 sebagai template engine. Jinja2 memungkinkan Anda menulis HTML yang dinamis dengan sintaks yang mirip Python.

Template Dasar

HTML β€” templates/base.html
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}Flask App{% endblock %}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
    {% block extra_css %}{% endblock %}
</head>
<body>
    <nav class="navbar">
        <a href="{{ url_for('main.beranda') }}">FlaskApp</a>
        <ul>
            <li><a href="{{ url_for('main.beranda') }}">Beranda</a></li>
            <li><a href="{{ url_for('main.tentang') }}">Tentang</a></li>
            {% if current_user.is_authenticated %}
                <li><a href="{{ url_for('main.profil', username=current_user.username) }}">Profil</a></li>
            {% else %}
                <li><a href="{{ url_for('main.login') }}">Login</a></li>
            {% endif %}
        </ul>
    </nav>

    <!-- Flash messages -->
    {% with messages = get_flashed_messages(with_categories=true) %}
        {% if messages %}
            {% for category, message in messages %}
                <div class="alert alert-{{ category }}">{{ message }}</div>
            {% endfor %}
        {% endif %}
    {% endwith %}

    <main class="container">
        {% block content %}{% endblock %}
    </main>

    <footer>
        <p>&copy; 2026 FlaskApp</p>
    </footer>

    <script src="{{ url_for('static', filename='js/app.js') }}"></script>
    {% block extra_js %}{% endblock %}
</body>
</html>

Template Anak

HTML β€” templates/beranda.html
{% extends 'base.html' %}

{% block title %}Beranda{% endblock %}

{% block content %}
<h1>{{ judul }}</h1>
<p>{{ deskripsi }}</p>

<div class="artikel-grid">
    {% for artikel in artikel_list %}
    <article class="card">
        <h2><a href="{{ url_for('main.detail', id=artikel.id) }}">{{ artikel.judul }}</a></h2>
        <p class="meta">{{ artikel.tanggal|format_date }} oleh {{ artikel.penulis }}</p>
        <p>{{ artikel.konten|truncate(200) }}</p>
        <div class="tags">
            {% for tag in artikel.tags %}
                <span class="badge">{{ tag }}</span>
            {% endfor %}
        </div>
    </article>
    {% else %}
    <p class="empty">Belum ada artikel.</p>
    {% endfor %}
</div>

<!-- Macro untuk reusable component -->
{% macro card(judul, isi) %}
<div class="card">
    <h3>{{ judul }}</h3>
    <p>{{ isi }}</p>
</div>
{% endmacro %}

{{ card("Tips Flask", "Selalu gunakan virtual environment!") }}
{{ card("Tips Jinja2", "Gunakan macro untuk komponen yang sering dipakai") }}

{% endblock %}

Render Template dari View

Python β€” render_template
from flask import render_template, flash, redirect, url_for

@main_bp.route('/')
def beranda():
    artikel_list = Artikel.query.filter_by(status='published').all()
    return render_template('beranda.html',
        judul='Selamat Datang',
        deskripsi='Blog sederhana dengan Flask',
        artikel_list=artikel_list
    )

# Flash message
@main_bp.route('/submit', methods=['POST'])
def submit():
    # Proses data...
    flash('Data berhasil disimpan!', 'success')
    return redirect(url_for('main.beranda'))

5. Forms dan Input

Flask menggunakan extension Flask-WTF untuk menangani form, validasi, dan proteksi CSRF. Flask-WTF dibangun di atas library WTForms.

Python β€” app/forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SelectField, SubmitField, PasswordField
from wtforms.validators import DataRequired, Email, Length, EqualTo, URL

class LoginForm(FlaskForm):
    username = StringField('Username', validators=[
        DataRequired(message="Username wajib diisi"),
        Length(min=3, max=50)
    ])
    password = PasswordField('Password', validators=[
        DataRequired(message="Password wajib diisi"),
        Length(min=6)
    ])
    submit = SubmitField('Login')

class ArtikelForm(FlaskForm):
    judul = StringField('Judul', validators=[
        DataRequired(),
        Length(min=10, max=200, message="Judul 10-200 karakter")
    ])
    konten = TextAreaField('Konten', validators=[
        DataRequired(),
        Length(min=50, message="Konten minimal 50 karakter")
    ])
    kategori = SelectField('Kategori', coerce=int, validators=[DataRequired()])
    gambar_url = StringField('URL Gambar', validators=[URL()])
    submit = SubmitField('Simpan Artikel')

class KomentarForm(FlaskForm):
    nama = StringField('Nama', validators=[DataRequired(), Length(max=100)])
    email = StringField('Email', validators=[DataRequired(), Email()])
    isi = TextAreaField('Komentar', validators=[DataRequired(), Length(min=5)])
    submit = SubmitField('Kirim Komentar')
HTML β€” templates/form_artikel.html
{% extends 'base.html' %}

{% block title %}Buat Artikel{% endblock %}

{% block content %}
<h1>Buat Artikel Baru</h1>

<form method="POST" enctype="multipart/form-data" novalidate>
    {{ form.hidden_tag() }}

    {% for field in [form.judul, form.konten, form.kategori, form.gambar_url] %}
    <div class="form-group">
        {{ field.label(class='form-label') }}
        {{ field(class='form-control') }}
        {% for error in field.errors %}
            <span class="error">{{ error }}</span>
        {% endfor %}
    </div>
    {% endfor %}

    {{ form.submit(class='btn btn-primary') }}
</form>
{% endblock %}
Python β€” routes (form handling)
@main_bp.route('/artikel/baru', methods=['GET', 'POST'])
@login_required
def buat_artikel():
    form = ArtikelForm()
    form.kategori.choices = [(k.id, k.nama) for k in Kategori.query.all()]

    if form.validate_on_submit():
        artikel = Artikel(
            judul=form.judul.data,
            konten=form.konten.data,
            kategori_id=form.kategori.data,
            gambar_url=form.gambar_url.data,
            penulis=current_user
        )
        db.session.add(artikel)
        db.session.commit()
        flash('Artikel berhasil dibuat!', 'success')
        return redirect(url_for('main.detail', id=artikel.id))

    return render_template('form_artikel.html', form=form)

6. Database dengan SQLAlchemy

Flask menggunakan Flask-SQLAlchemy sebagai ORM untuk berinteraksi dengan database. SQLAlchemy mendukung SQLite, PostgreSQL, MySQL, dan lainnya.

Python β€” app/models.py
from app import db
from datetime import datetime
from flask_login import UserMixin

class User(UserMixin, db.Model):
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password_hash = db.Column(db.String(256), nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    # Relationship
    artikel = db.relationship('Artikel', backref='penulis', lazy='dynamic')

    def __repr__(self):
        return f'<User {self.username}>'

class Kategori(db.Model):
    __tablename__ = 'kategori'

    id = db.Column(db.Integer, primary_key=True)
    nama = db.Column(db.String(100), unique=True, nullable=False)
    slug = db.Column(db.String(100), unique=True, nullable=False)

    def __repr__(self):
        return f'<Kategori {self.nama}>'

class Artikel(db.Model):
    __tablename__ = 'artikel'

    id = db.Column(db.Integer, primary_key=True)
    judul = db.Column(db.String(200), nullable=False)
    slug = db.Column(db.String(200), unique=True, nullable=False)
    konten = db.Column(db.Text, nullable=False)
    status = db.Column(db.String(20), default='draft')
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

    # Foreign Keys
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    kategori_id = db.Column(db.Integer, db.ForeignKey('kategori.id'))

    # Relationship
    komentar = db.relationship('Komentar', backref='artikel', lazy='dynamic',
                                cascade='all, delete-orphan')

    def __repr__(self):
        return f'<Artikel {self.judul}>'

class Komentar(db.Model):
    __tablename__ = 'komentar'

    id = db.Column(db.Integer, primary_key=True)
    nama = db.Column(db.String(100), nullable=False)
    email = db.Column(db.String(120), nullable=False)
    isi = db.Column(db.Text, nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    artikel_id = db.Column(db.Integer, db.ForeignKey('artikel.id'), nullable=False)

CRUD Operations

Python β€” CRUD
from app import db
from app.models import Artikel, Kategori, User

# CREATE β€” Tambah data baru
kategori = Kategori(nama='Python', slug='python')
db.session.add(kategori)
db.session.commit()

artikel = Artikel(
    judul='Tutorial Flask',
    slug='tutorial-flask',
    konten='Ini konten tutorial...',
    user_id=1,
    kategori_id=1
)
db.session.add(artikel)
db.session.commit()

# READ β€” Query data
semua = Artikel.query.all()                     # Semua data
artikel = Artikel.query.get(1)                   # By ID
artikel = Artikel.query.filter_by(status='published').first()  # Filter
artikel = Artikel.query.filter(Artikel.judul.contains('Flask'))  # Search

# Order, limit, paginate
terbaru = Artikel.query.order_by(Artikel.created_at.desc()).limit(10).all()
page = Artikel.query.paginate(page=1, per_page=10, error_out=False)

# Join
artikel_kategori = Artikel.query.join(Kategori).filter(Kategori.nama == 'Python').all()

# UPDATE β€” Ubah data
artikel = Artikel.query.get(1)
artikel.judul = 'Judul Baru'
artikel.status = 'published'
db.session.commit()

# DELETE β€” Hapus data
artikel = Artikel.query.get(1)
db.session.delete(artikel)
db.session.commit()

7. Membuat REST API

Flask sangat cocok untuk membuat REST API karena sifatnya yang minimalis. Berikut contoh membuat API lengkap dengan CRUD.

Python β€” app/api.py
from flask import Blueprint, request, jsonify
from app.models import Artikel, db
from app import db

api_bp = Blueprint('api', __name__)

# GET semua artikel
@api_bp.route('/artikel', methods=['GET'])
def get_artikel():
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 10, type=int)
    status = request.args.get('status', 'published')

    query = Artikel.query.filter_by(status=status)
    pagination = query.paginate(page=page, per_page=per_page)

    return jsonify({
        'artikel': [{
            'id': a.id,
            'judul': a.judul,
            'slug': a.slug,
            'konten': a.konten[:200] + '...',
            'penulis': a.penulis.username,
            'created_at': a.created_at.isoformat()
        } for a in pagination.items],
        'total': pagination.total,
        'pages': pagination.pages,
        'current_page': pagination.page
    })

# GET satu artikel
@api_bp.route('/artikel/<int:id>', methods=['GET'])
def get_artikel_by_id(id):
    artikel = Artikel.query.get_or_404(id)
    return jsonify({
        'id': artikel.id,
        'judul': artikel.judul,
        'slug': artikel.slug,
        'konten': artikel.konten,
        'penulis': artikel.penulis.username,
        'kategori': artikel.kategori.nama if artikel.kategori else None,
        'komentar': [{
            'nama': k.nama,
            'isi': k.isi,
            'tanggal': k.created_at.isoformat()
        } for k in artikel.komentar.all()],
        'created_at': artikel.created_at.isoformat()
    })

# POST β€” buat artikel baru
@api_bp.route('/artikel', methods=['POST'])
def create_artikel():
    data = request.get_json()

    if not data or not data.get('judul') or not data.get('konten'):
        return jsonify({'error': 'judul dan konten wajib diisi'}), 400

    artikel = Artikel(
        judul=data['judul'],
        slug=data['judul'].lower().replace(' ', '-'),
        konten=data['konten'],
        user_id=data.get('user_id', 1),
        status=data.get('status', 'draft')
    )

    db.session.add(artikel)
    db.session.commit()

    return jsonify({
        'message': 'Artikel berhasil dibuat',
        'id': artikel.id
    }), 201

# PUT β€” update artikel
@api_bp.route('/artikel/<int:id>', methods=['PUT'])
def update_artikel(id):
    artikel = Artikel.query.get_or_404(id)
    data = request.get_json()

    if data.get('judul'):
        artikel.judul = data['judul']
    if data.get('konten'):
        artikel.konten = data['konten']
    if data.get('status'):
        artikel.status = data['status']

    db.session.commit()
    return jsonify({'message': 'Artikel berhasil diupdate'})

# DELETE β€” hapus artikel
@api_bp.route('/artikel/<int:id>', methods=['DELETE'])
def delete_artikel(id):
    artikel = Artikel.query.get_or_404(id)
    db.session.delete(artikel)
    db.session.commit()
    return jsonify({'message': 'Artikel berhasil dihapus'})

# Error handler untuk API
@api_bp.errorhandler(404)
def not_found(error):
    return jsonify({'error': 'Resource tidak ditemukan'}), 404

@api_bp.errorhandler(500)
def server_error(error):
    return jsonify({'error': 'Internal server error'}), 500

8. Deployment

Setelah aplikasi selesai, saatnya deploy ke server produksi. Berikut beberapa opsi deployment Flask.

Konfigurasi untuk Produksi

Python β€” app/config.py
import os

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'rahasia-produksi-ganti-ini'
    SQLALCHEMY_TRACK_MODIFICATIONS = False

class DevelopmentConfig(Config):
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///dev.db'

class ProductionConfig(Config):
    DEBUG = False
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
        'postgresql://user:pass@localhost/flask_prod'

class TestingConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///test.db'

Docker Deployment

Dockerfile
# Dockerfile
FROM python:3.12-slim

WORKDIR /app

# Instal dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy source code
COPY . .

# Expose port
EXPOSE 5000

# Jalankan dengan Gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "run:app"]
docker-compose.yml
# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "5000:5000"
    environment:
      - FLASK_ENV=production
      - DATABASE_URL=postgresql://postgres:password@db:5432/flask_app
      - SECRET_KEY=your-secret-key-here
    depends_on:
      - db

  db:
    image: postgres:15
    environment:
      - POSTGRES_DB=flask_app
      - POSTGRES_PASSWORD=password
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

Deployment Platforms

Platform Gratis? Keunggulan
Renderβœ… YaMudah setup, auto-deploy dari Git
Railwayβœ… Ya (trial)Deploy cepat, DB managed
Vercelβœ… YaCDN global, serverless functions
PythonAnywhereβœ… YaSpesialis Python, mudah setup
AWS / GCP / AzureπŸ’° BayarSkalabilitas enterprise

9. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Flask:

Pertanyaan 1: Mengapa Flask disebut "micro" framework?

a) Karena ukuran file download-nya kecil
b) Karena inti framework minimalis dan fitur lain ditambahkan via extension
c) Karena hanya bisa membuat aplikasi kecil
d) Karena tidak mendukung database

Pertanyaan 2: Template engine apa yang digunakan oleh Flask?

a) Django Template Language
b) Handlebars
c) Jinja2
d) Mako

Pertanyaan 3: Apa fungsi dari @app.route() di Flask?

a) Untuk mengkonfigurasi database
b) Untuk memetakan URL ke fungsi view
c) Untuk mendefinisikan model data
d) Untuk mengatur middleware

Pertanyaan 4: Apa fungsi dari Blueprint di Flask?

a) Untuk mengatur warna tema aplikasi
b) Untuk mengorganisir routes dan views ke dalam kelompok modular
c) Untuk mengelola file CSS
d) Untuk membuat database schema

Pertanyaan 5: HTTP method apa yang digunakan untuk mengirim data form ke server?

a) GET
b) POST
c) PUT
d) DELETE