Skip to content

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 flask

Creating 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_id

Typed (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,
}
ConverterDescription
stringDefault — any text without a forward slash
intPositive integers
floatPositive floating-point values
pathLike string but also accepts slashes
uuidUUID strings, e.g. xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
@app.route('/user/<int:user_id>')
def user_info(user_id):
    return 'hello %d' % user_id

Custom 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 BaseConverter

Step 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 rule

Step 3 — Register the converter:

app.url_map.converters['re'] = RegexConverter

Step 4 — Use it in a route:

@app.route("/login/<re('1\d{10}'):mobile>")
def login(mobile):
    return mobile

A 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 mobile

Restricting 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.method

HTTP Requests

Import the global request proxy object from Flask:

from flask import request

request represents the current HTTP request inside a view function.

Common request Attributes

AttributeDescriptionType
dataRaw request body (bytes), for content types Flask cannot recognizebytes
formHTML form data (POST body, application/x-www-form-urlencoded)ImmutableMultiDict
argsQuery string parametersImmutableMultiDict
cookiesCookie datadict
headersRequest headersEnvironHeaders
methodHTTP method ('GET', 'POST', etc.)str
urlFull request URLstr
filesUploaded filesImmutableMultiDict
jsonParsed 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 response

Session 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 response

Getting 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 response

Sessions

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"
Flask’s default session implementation stores session data client-side in a signed cookie (not on the server). This is secure as long as SECRET_KEY is strong and kept secret. For server-side storage (Redis, database), use the Flask-Session extension.
Last updated on