Getting Started with Flask Web Framework

Comparing Flask, Django, and Tornado

Django is a full-stack framework packed with built-in components including ORM, Forms, ModelForms, caching, sessions, middleware, signals, and more.

Flask is lightweight and minimal, offering core functionality without bundled components but with extensive third-party extension support. Its routing uses decorators but ultimately relies on add_url_rule internally.

Tornado is an asynchronous non-blocking framework built on IOLoop using coroutines for async operations.


The Foundation: WSGI Examples

Werkzeug Implementation

from werkzeug.wrappers import Request, Response

@Request.application
def handle_request(request):
    return Response('Hello World!')

if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('localhost', 4000, handle_request)

Using wsgiref

from wsgiref.simple_server import make_server

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b'<h1>Hello, web!</h1>']

if __name__ == '__main__':
    server = make_server('', 8000, application)
    server.serve_forever()

Raw Socket Implementation

import socket

def handle_connection(client):
    data = client.recv(1024)
    client.send("HTTP/1.1 200 OK\r\n\r\n")
    client.send("Hello, World")

def main():
    server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_sock.bind(('localhost', 8000))
    server_sock.listen(5)

    while True:
        conn, addr = server_sock.accept()
        handle_connection(conn)
        conn.close()

if __name__ == '__main__':
    main()

All three approaches demonstrate that web frameworks fundamentally operate over sockets.


Installation

pip3 install flask


Basic Flask Application

from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return 'Hello World!'

if __name__ == '__main__':
    app.run()


Building a Login System

Main application file:

from flask import Flask, render_template, request, redirect, session, url_for

app = Flask(__name__)
app.debug = True
app.secret_key = 'your_secret_key_here'

USERS = {
    1: {'name': 'Alice', 'age': 25, 'gender': 'Female', 'text': 'Sample bio text here...'},
    2: {'name': 'Bob', 'age': 30, 'gender': 'Male', 'text': 'Another sample bio...'},
    3: {'name': 'Charlie', 'age': 22, 'gender': 'Male', 'text': 'More sample content...'},
}

def require_login(func):
    def wrapper(*args, **kwargs):
        if 'username' not in session:
            return redirect(url_for('login_view'))
        return func(*args, **kwargs)
    return wrapper

@app.route('/user/<int:user_id>', methods=['GET'], endpoint='user_detail')
@require_login
def user_detail(user_id):
    user = USERS.get(user_id)
    return render_template('detail.html', user=user)

@app.route('/dashboard', methods=['GET'], endpoint='dashboard')
@require_login
def dashboard():
    username = session.get('username')
    return render_template('dashboard.html', users=USERS)

@app.route('/login', methods=['GET', 'POST'], endpoint='login_view')
def login_view():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        username = request.form.get('username')
        password = request.form.get('password')
        if username == 'admin' and password == 'secret':
            session['username'] = username
            return redirect(url_for('dashboard'))
        return render_template('login.html', error='Invalid credentials')

if __name__ == '__main__':
    app.run()

login.html:

<html>
<head><meta charset="UTF-8"><title>Login</title></head>
<body>
    <h1>Sign In</h1>
    <form method="post">
        <input type="text" name="username" placeholder="Username">
        <input type="password" name="password" placeholder="Password">
        <input type="submit" value="Login">
    </form>
    {{ error if error else '' }}
</body>
</html>

dashboard.html:

<html>
<head><meta charset="UTF-8"><title>Dashboard</title></head>
<body>
    <h1>User Directory</h1>
    | {{ id }} | {{ data.name }} | {{ data\['name'\] }} | {{ data.get('name') }} | [View Profile](</user/{{ id }}>) |
|---|---|---|---|---|
</body>
</html>

detail.html:

<html>
<head><meta charset="UTF-8"><title>User Profile</title></head>
<body>
    <h1>{{ user.name }}'s Profile</h1>
    <p>{{ user.text }}</p>
</body>
</html>

Test URLs:

  • Login: http://127.0.0.1:5000/login
  • Dashboard: http://127.0.0.1:5000/dashboard
  • Detail page: http://127.0.0.1:5000/user/1

Configuration Management

Flask configuration is stored in a dictionary-like config.Config object.

Default settings include:

  • DEBUG: Enable debug mode
  • TESTING: Enable testing mode
  • SECRET_KEY: Required for session management
  • PERMANENT_SESSION_LIFETIME: Session duration (default: 31 days)
  • SESSION_COOKIE_HTTPONLY: Cookie security flag
  • MAX_CONTENT_LENGTH: Maximum request size

Configuration Methods

Method 1 - Direct assignment:

app.config['DEBUG'] = True
app.config.update(DEBUG=True, SECRET_KEY='abc')

Method 2 - Load from Python file:

app.config.from_pyfile('settings.py')

Method 3 - Environment variable:

app.config.from_envvar('FLASK_CONFIG')

Method 4 - JSON file:

app.config.from_json('config.json')

Method 5 - Dictionary:

app.config.from_mapping({'DEBUG': True, 'SECRET_KEY': 'key'})

Method 6 - Class:

class BaseConfig:
    DEBUG = False
    SECRET_KEY = 'base_key'

class DevConfig(BaseConfig):
    DEBUG = True

class ProdConfig(BaseConfig):
    DATABASE_URI = 'mysql://user@localhost/db'

app.config.from_object(DevConfig)

config.py:

class Config:
    DEBUG = False
    TESTING = False
    SECRET_KEY = 'production_secret'
    DATABASE_URI = 'sqlite://:memory:'

class ProductionConfig(Config):
    DATABASE_URI = 'mysql://user@db/prod'

class DevelopmentConfig(Config):
    DEBUG = True

app.py:

from flask import Flask
from config import DevelopmentConfig

app = Flask(__name__)
app.config.from_object(DevelopmentConfig)

@app.route('/')
def index():
    return 'Hello World!'

if __name__ == '__main__':
    app.run()


Route Mechanics

The @app.route decorator is syntactic sugar for add_url_rule:

# This decorator:
@app.route('/', methods=['GET', 'POST'], endpoint='home')
def home():
    return 'Home'

# Equates to:
def home():
    return 'Home'
app.add_url_rule('/', 'home', home, methods=['GET', 'POST'])

Alternative approach:

from flask import Flask

app = Flask(__name__)

def login():
    return 'Login Page'

app.add_url_rule('/login', 'login_page', login, methods=['GET', 'POST'])

if __name__ == '__main__':
    app.run()


Class-Based Views

Class views offer inheritance and better organization compared to function-based views.

from flask import Flask, views

app = Flask(__name__)
app.debug = True
app.secret_key = 'secret'

def authentication(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

class APIView(views.MethodView):
    methods = ['GET']  # Restrict to GET requests
    decorators = [authentication]  # Apply decorator

    def get(self):
        return 'API GET Response'

    def post(self):
        return 'API POST Response'

app.add_url_rule('/api', view_func=APIView.as_view(name='api'))

if __name__ == '__main__':
    app.run()


URL Rule Parameters

Redirect Example

from flask import Flask

app = Flask(__name__)

@app.route('/old-page/', endpoint='old', redirect_to='/new-page/')
def old_page():
    return 'Old Page (should redirect)'

@app.route('/new-page/', endpoint='new', defaults={'page_id': 1}, strict_slashes=True)
def new_page(page_id):
    return f'New Page (ID: {page_id})'

if __name__ == '__main__':
    app.run()

Host File Configuration

For local testing, modify your hosts file:

  • Windows: C:\Windows\System32\drivers\etc\hosts
  • Linux/Mac: /etc/hosts

Add entries like:

127.0.0.1:5000  www.example.com
127.0.0.1       api.example.com

Subdomain Routing

from flask import Flask, url_for

app = Flask(__name__)
app.config['SERVER_NAME'] = 'example.com:5000'

@app.route('/', subdomain='admin')
def admin_static():
    return 'Admin Dashboard'

@app.route('/profile', subdomain='<username>')
def user_profile(username):
    return f'{username}\'s Profile'

if __name__ == '__main__':
    app.run()

Access at http://admin.example.com:5000/ for static subdomain or http://john.example.com:5000/profile for dynamic subdomain.


Custom URL Converters

Regex Converter

Step 1: Create a converter class inheriting from BaseConverterStep 2: Override to_python for input processing Step 3: Override to_url for reverse URL generation

from flask import Flask, url_for
from werkzeug.routing import BaseConverter

app = Flask(__name__)

class NumericConverter(BaseConverter):
    def __init__(self, url_map, regex_pattern):
        super().__init__(url_map)
        self.regex = regex_pattern

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return super().to_url(value)

app.url_map.converters['digits'] = NumericConverter

@app.route('/item/<digits:"\\d+":item_id>')
def get_item(item_id):
    print(f'Item ID type: {type(item_id)}')
    url_for('get_item', item_id=42)
    return f'Item #{item_id}'

if __name__ == '__main__':
    app.run()


Template Features

Passing Functions to Templates

from flask import Flask, render_template, Markup

app = Flask(__name__)

def generate_input(field_name):
    return Markup(f'<input type="text" name="{field_name}">')

@app.route('/')
def index():
    return render_template('form.html', input_generator=generate_input)

if __name__ == '__main__':
    app.run()

form.html:

<html>
<head><meta charset="UTF-8"><title>Form</title></head>
<body>
    {{ input_generator('username') }}

    {% macro create_field(name, field_type='text', default='') %}
        <input type="{{ field_type }}" name="{{ name }}_1">
        <input type="{{ field_type }}" name="{{ name }}_2">
        <input type="{{ field_type }}" name="{{ name }}_3">
    {% endmacro %}

    {{ create_field('email') }}
</body>
</html>


Request and Response Handling

Request object (imported from flask): request

Response options:

  • jsonify(): Return JSON responses
  • make_response(): Create custom responses
from flask import Flask, request, make_response, jsonify

app = Flask(__name__)

@app.route('/set-cookie')
def set_cookie():
    resp = make_response('Cookie Set')
    resp.set_cookie('user_id', '12345')
    return resp

@app.route('/json')
def return_json():
    return jsonify({'status': 'success', 'data': [1, 2, 3]})

if __name__ == '__main__':
    app.run()


Sesion Management

from flask import Flask, session

app = Flask(__name__)
app.secret_key = 'secure_random_key'

@app.route('/save')
def save_data():
    session['user'] = 'john_doe'
    session['role'] = 'admin'
    return 'Data saved to session'

@app.route('/read')
def read_data():
    username = session.get('user', 'guest')
    return f'Welcome, {username}'

if __name__ == '__main__':
    app.run()

Sessions in Flask use signed cookies for storage, keeping data secure on the client side.

Tags: Flask python web WSGI Framework

Posted on Sat, 11 Jul 2026 17:07:47 +0000 by theeper