Flask Fundamentals
Flask was born in 2010. Created by Armin Ronacher on top of the Werkzeug toolkit, it is a lightweight web development framework. The current stable version is Flask 3.x (requires Python 3.8+).
Flask itself only provides core functionality (routing, request/response, templates). Everything else — databases, authentication, forms — is handled through extensions. This design makes Flask highly flexible, suitable for anything from small APIs to medium-sized web applications.
Its WSGI toolkit is Werkzeug (routing and requests); its template engine is Jinja2. These two libraries are Flask’s core dependencies.
Official docs: flask.palletsprojects.com
Commonly used Flask extensions:
- Flask-SQLAlchemy — database ORM
- Flask-Script — command-line script tool / scaffolding
- Flask-Migrate — database migration management
- Flask-Session — configurable session storage backends
- Flask-WTF — form validation with CSRF protection
- Flask-Mail — email sending
- Flask-Babel — internationalization and localization
- Flask-Login — user authentication state management
- Flask-OpenID — OAuth / OpenID authentication
- Flask-RESTful — REST API development toolkit
- Flask-Bootstrap — integrates Twitter Bootstrap
- Flask-Moment — localized date and time display
- Flask-Admin — simple extensible admin interface
More extensions are available on PyPI under the Flask classifier.
Installation
# Create a virtual environment (recommended)
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# Install Flask (current stable version 3.x)
pip install flaskCreating a Flask Project
Unlike Django, Flask provides no scaffolding command. You create the project structure manually.
Create a main application file — it can be named app.py, run.py, or main.py:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
if __name__ == '__main__':
app.run()Annotated version:
from flask import Flask
"""
Flask(import_name, ...)
import_name The package/module where the Flask app lives. Pass __name__.
This determines where Flask looks for static files and templates.
static_path Static file access path (deprecated; use static_url_path instead)
static_url_path URL prefix for static files. Default: '/' + static_folder
static_folder Folder for static files. Default: 'static'
template_folder Folder for template files. Default: 'templates'
"""
app = Flask(import_name=__name__)
# Routes in Flask are written as decorators on view functions.
# Flask allows returning HTML strings directly from view functions.
@app.route('/')
def index():
return "<h1>hello world</h1>"
# Configuration class
class Config(object):
DEBUG = True # enable debug mode
# Load configuration — the class-based approach is the most common
app.config.from_object(Config)
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000)Routing
Route names and view function names must be globally unique; duplicates cause an error.
@app.route('/demo1')
def demo1():
return 'demo1'Route Parameters — Two Approaches
Route parameters are parts of the URL path itself.
Untyped (accepts anything):
@app.route('/user/<user_id>')
def user_info(user_id):
return 'hello %s' % user_idTyped (with a converter):
Flask’s built-in converters are defined in werkzeug.routing:
DEFAULT_CONVERTERS = {
"default": UnicodeConverter, # same as "string"
"string": UnicodeConverter,
"any": AnyConverter,
"path": PathConverter,
"int": IntegerConverter,
"float": FloatConverter,
"uuid": UUIDConverter,
}| Converter | Description |
|---|---|
string | Default — any text without a forward slash |
int | Positive integers |
float | Positive floating-point values |
path | Like string but also accepts slashes |
uuid | UUID strings, e.g. xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
@app.route('/user/<int:user_id>')
def user_info(user_id):
return 'hello %d' % user_idCustom Route Converters (Regex Matching)
When the built-in converters are not precise enough, create a custom one:
Step 1 — Import the base converter:
from werkzeug.routing import BaseConverterStep 2 — Create a custom converter class:
class RegexConverter(BaseConverter):
"""Matches based on an arbitrary regular expression."""
def __init__(self, map, *args):
super().__init__(map)
self.regex = args[0] # the regex is passed as an argument in the URL ruleStep 3 — Register the converter:
app.url_map.converters['re'] = RegexConverterStep 4 — Use it in a route:
@app.route("/login/<re('1\d{10}'):mobile>")
def login(mobile):
return mobileA common pattern is a dedicated phone-number converter:
from werkzeug.routing import BaseConverter
class MobileConverter(BaseConverter):
"""Matches Chinese mobile phone numbers."""
def __init__(self, map, *args):
super().__init__(map)
self.regex = "1[3-9]\d{9}"
app.url_map.converters['mob'] = MobileConverter
@app.route('/user/<mob:mobile>')
def user(mobile):
return mobileRestricting HTTP Methods
By default, a route only accepts GET. Use the methods argument to allow others:
@app.route("/user", methods=["POST", "PUT", "GET", "DELETE", "PATCH"])
def user():
print(request.method) # 'GET', 'POST', etc.
print(request.query_string) # b'user=1'
print(request.path) # /user
print(request.url) # http://127.0.0.1:5000/user?user=1
return request.methodHTTP Requests
Import the global request proxy object from Flask:
from flask import requestrequest represents the current HTTP request inside a view function.
Common request Attributes
| Attribute | Description | Type |
|---|---|---|
data | Raw request body (bytes), for content types Flask cannot recognize | bytes |
form | HTML form data (POST body, application/x-www-form-urlencoded) | ImmutableMultiDict |
args | Query string parameters | ImmutableMultiDict |
cookies | Cookie data | dict |
headers | Request headers | EnvironHeaders |
method | HTTP method ('GET', 'POST', etc.) | str |
url | Full request URL | str |
files | Uploaded files | ImmutableMultiDict |
json | Parsed JSON body (when Content-Type: application/json) | dict / None |
Accessing Request Data
@app.route("/args", methods=["POST", "GET"])
def args():
# Query string: http://127.0.0.1:5000/args?name=xiaoming&lve=swimming&lve=shopping
print(request.args) # ImmutableMultiDict([('name', 'xiaoming'), ...])
print(request.args["name"]) # 'xiaoming'
print(request.args.get("name")) # 'xiaoming'
print(request.args.getlist("lve")) # ['swimming', 'shopping']
print(request.args.to_dict(flat=True)) # {'name': 'xiaoming', 'lve': 'swimming'}
return "ok"
@app.route("/data", methods=["POST", "PUT", "PATCH"])
def data():
print(request.form) # form-encoded body
print(request.json) # parsed JSON body
avatar = request.files["avatar"] # uploaded file
print(request.headers.get("Host"))
print(request.url)
return "ok"HTTP Responses
Flask supports two response styles:
- Data responses — return HTML text, JSON, or other content
- Page redirects — redirect to another URL
Return HTML
from flask import make_response
@app.route("/")
def index():
return "<img src='https://example.com/logo.png'>"
# Or equivalently:
# return make_response("<h1>hello user</h1>")Return JSON
Use jsonify to serialize Python data to JSON:
from flask import jsonify
@app.route("/api/users")
def users():
data = [
{"id": 1, "username": "alice", "age": 18},
{"id": 2, "username": "bob", "age": 17},
]
return jsonify(data)Redirects
from flask import redirect, url_for
# Redirect to an external URL
@app.route("/external")
def external():
return redirect("https://www.example.com")
# Redirect to another view using url_for
@app.route("/login")
def login():
return redirect(url_for("index"))
# Redirect to a view with parameters
@app.route('/user/<int:user_id>')
def user_info(user_id):
return 'hello %d' % user_id
@app.route('/go-to-user')
def go_to_user():
return redirect(url_for("user_info", user_id=100))Custom Status Codes and Response Headers
@app.route('/custom')
def custom():
return 'Custom response', 400
from flask import make_response
@app.route("/rep")
def rep():
response = make_response("ok")
response.headers["Company"] = "acme" # custom response header
response.status_code = 201
return responseSession Control (Cookies and Sessions)
HTTP is stateless — the server has no memory of previous requests. Cookies and sessions are the main tools for maintaining state.
Cookies
Cookies are generated by the server and stored in the browser. On subsequent requests, the browser automatically sends them back. Cookie data is stored on the client side.
Use cases: login state, browsing history, shopping cart items.
Setting a Cookie
from flask import make_response
@app.route("/set_cookie")
def set_cookie():
response = make_response("ok")
# set_cookie(key, value, max_age_in_seconds)
response.set_cookie("username", "xiaoming", 100)
# If max_age is omitted, the cookie expires when the browser session ends
response.set_cookie("age", "100")
return responseGetting a Cookie
@app.route("/get_cookie")
def get_cookie():
print(request.cookies)
print(request.cookies.get("username"))
return ""Deleting a Cookie
@app.route("/del_cookie")
def del_cookie():
response = make_response("ok")
# Set max_age to 0 to expire the cookie immediately
response.set_cookie("username", "", 0)
return responseSessions
For sensitive data (usernames, balances, verification codes), use sessions which store data on the server side. The browser only receives a session ID in a cookie.
Flask sessions require a SECRET_KEY for encryption:
from flask import Flask, session, make_response, request
app = Flask(__name__)
class Config():
SECRET_KEY = "your-secret-key-here"
DEBUG = True
app.config.from_object(Config)Setting Session Data
@app.route("/set_session")
def set_session():
session["username"] = "xiaohuihui"
session["info"] = {"age": 11, "active": True}
return "ok"Getting Session Data
@app.route("/get_session")
def get_session():
print(session.get("username"))
print(session.get("info"))
return "ok"Deleting Session Data
@app.route("/del_session")
def del_session():
try:
del session["username"]
# session.clear() # delete all session data
except KeyError:
pass
return "ok"SECRET_KEY is strong and kept secret. For server-side storage (Redis, database), use the Flask-Session extension.