11.1 — Introduction to Flask
What is Flask?
Flask is a lightweight Python micro-framework for building web applications and REST APIs. Created by Armin Ronacher in 2010, it deliberately stays minimal — providing routing, request handling, and templating without forcing an ORM, authentication library, or form validator. You add only what you need.
┌─────────────────────────────────────────────────────┐
│ Flask Architecture │
│ │
│ Browser/Client │
│ │ HTTP Request │
│ ▼ │
│ ┌─────────────┐ ┌──────────────┐ │
│ │ Flask App │──▶│ URL Router │ │
│ │ (WSGI) │ │ (url_map) │ │
│ └─────────────┘ └──────┬───────┘ │
│ │ match │
│ ┌──────▼───────┐ │
│ │ View Func │ │
│ │ (Python fn) │ │
│ └──────┬───────┘ │
│ ┌────────────────┼─────────────┐ │
│ ▼ ▼ ▼ │
│ ┌────────────┐ ┌──────────────┐ ┌────────┐ │
│ │ Jinja2 │ │ JSON/Data │ │ DB │ │
│ │ Templates │ │ (REST API) │ │ Query │ │
│ └────────────┘ └──────────────┘ └────────┘ │
│ │ │
│ ▼ │
│ HTTP Response ──▶ Browser/Client │
└─────────────────────────────────────────────────────┘
Installation & First App
mkdir flask-project && cd flask-project
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install flask
Create app.py:
from flask import Flask
app = Flask(__name__) # __name__ tells Flask where to find resources
@app.route("/") # URL pattern
def home():
return "Hello, Flask!" # Response body (plain text)
@app.route("/about")
def about():
return "<h1>About Page</h1><p>Built with Flask.</p>"
if __name__ == "__main__":
app.run(debug=True) # debug=True: auto-reload + detailed errors
python3 app.py
# → Running on http://127.0.0.1:5000
Important: Never use
debug=Truein production. It exposes an interactive debugger.
Routing
URL Patterns and Variables
from flask import Flask
app = Flask(__name__)
# Static routes
@app.route("/users")
def users():
return "User list"
# Dynamic segments — <variable_name>
@app.route("/users/<int:user_id>")
def get_user(user_id): # Flask injects the captured value
return f"User #{user_id}"
@app.route("/users/<string:username>")
def profile(username):
return f"Profile: {username}"
# Slug with slashes
@app.route("/docs/<path:subpath>")
def docs(subpath):
return f"Docs path: {subpath}"
Converters: string (default), int, float, path (allows /), uuid
HTTP Methods
By default, routes only accept GET. Use the methods parameter:
from flask import Flask, request, redirect, url_for
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get("username")
password = request.form.get("password")
# validate…
return redirect(url_for("dashboard")) # PRG pattern
return """
<form method="POST">
<input name="username" placeholder="Username">
<input name="password" type="password" placeholder="Password">
<button type="submit">Login</button>
</form>
"""
@app.route("/dashboard")
def dashboard():
return "Welcome to the dashboard!"
url_for()generates URLs by function name — if you change a URL pattern, links auto-update.
The Request Object
Flask's global request object exposes everything about the incoming HTTP request:
from flask import request
@app.route("/search")
def search():
# Query parameters: /search?q=python&page=2
q = request.args.get("q", "") # default "" if missing
page = request.args.get("page", 1, type=int)
# Form data (POST with Content-Type: application/x-www-form-urlencoded)
# request.form.get("field_name")
# JSON body (POST with Content-Type: application/json)
# data = request.get_json()
# File uploads
# f = request.files.get("upload")
# Headers
ua = request.headers.get("User-Agent", "unknown")
return f"Searching '{q}', page {page}, UA: {ua}"
Jinja2 Templates
Instead of returning raw HTML strings, use Jinja2 templates (Flask includes it by default).
Project structure
flask-project/
├── app.py
├── templates/
│ ├── base.html
│ └── index.html
└── static/
├── style.css
└── logo.png
templates/base.html — layout template
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My Site{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<nav>
<a href="{{ url_for('home') }}">Home</a>
<a href="{{ url_for('about') }}">About</a>
</nav>
<main>
{% block content %}{% endblock %} {# child templates fill this block #}
</main>
<footer>© 2025 My Flask App</footer>
</body>
</html>
templates/index.html — child template
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block content %}
<h1>Welcome, {{ username }}!</h1>
{% if items %}
<ul>
{% for item in items %}
<li>{{ item.name }} — ${{ item.price | round(2) }}</li>
{% endfor %}
</ul>
{% else %}
<p>No items found.</p>
{% endif %}
{% endblock %}
Rendering templates in Python
from flask import Flask, render_template
@app.route("/")
def home():
products = [
{"name": "Laptop", "price": 999.99},
{"name": "Keyboard", "price": 49.95},
]
return render_template("index.html",
username="Alice",
items=products)
Key Jinja2 syntax:
| Syntax | Purpose |
|---|---|
{{ variable }} | Output a value (auto-escaped for XSS safety) |
{% if %}…{% endif %} | Conditional block |
{% for x in list %}…{% endfor %} | Loop |
{% extends "base.html" %} | Template inheritance |
{% block name %}…{% endblock %} | Overrideable block |
{{ value | filter }} | Apply a filter (e.g., upper, round, length) |
Static Files
Place assets in the static/ folder:
# In templates: url_for('static', filename='style.css')
# Generates: /static/style.css
# In Python:
from flask import send_from_directory
@app.route("/downloads/<filename>")
def download(filename):
return send_from_directory("downloads", filename, as_attachment=True)
Response Objects
from flask import make_response, jsonify, redirect, url_for
# Custom headers & cookies
@app.route("/cookie")
def set_cookie():
resp = make_response("Cookie set!")
resp.set_cookie("session_id", "abc123", httponly=True, samesite="Lax")
return resp
# JSON response
@app.route("/api/status")
def status():
return jsonify({"status": "ok", "version": "1.0"}) # sets Content-Type: application/json
# Redirect
@app.route("/old-url")
def old_url():
return redirect(url_for("home"), code=301) # permanent redirect
# Custom status code
@app.route("/forbidden")
def forbidden():
return "Access denied", 403
Error Handlers
@app.errorhandler(404)
def not_found(error):
return render_template("404.html"), 404
@app.errorhandler(500)
def server_error(error):
return jsonify({"error": "Internal server error"}), 500
Key Vocabulary
| Term | Definition |
|---|---|
| Route | URL pattern mapped to a Python function |
| View function | Python function that handles a request and returns a response |
| WSGI | Web Server Gateway Interface — Python web standard |
| Jinja2 | Flask's templating engine |
| Template inheritance | Base + child template pattern to avoid duplication |
url_for() | Helper that generates URLs by function name |
request | Thread-local global object with all HTTP request data |
debug=True | Auto-reload and interactive debugger (dev only!) |
Summary
Flask(__name__)creates the application@app.route("/path", methods=["GET","POST"])maps URLs to functions<int:id>captures and converts dynamic URL segmentsrequest.args,request.form,request.get_json()access request datarender_template("file.html", key=value)renders Jinja2 templatesurl_for("function_name")generates safe, dynamic URLsjsonify({...})returns JSON responses with the correctContent-Type
Next: 11.2 — REST APIs with Flask to build professional JSON APIs.