Flask

The Python micro framework for building web applications - lightweight and flexible. Simple yet extensible design for prototyping to production.

PythonframeworkBackendWebmicroframeworkWerkzeugJinja2

GitHub Overview

pallets/flask

The Python micro framework for building web applications.

Stars70,157
Watchers2,106
Forks16,518
Created:April 6, 2010
Language:Python
License:BSD 3-Clause "New" or "Revised" License

Topics

flaskjinjapalletspythonweb-frameworkwerkzeugwsgi

Star History

pallets/flask Star History
Data as of: 8/13/2025, 01:43 AM

Framework

Flask

Overview

Flask is a lightweight and flexible micro web framework written in Python. Its simple and extensible design allows you to select and use only the features you need, providing high flexibility.

Details

Flask is a Python micro web framework developed by Armin Ronacher in 2010. "Micro" doesn't mean limited functionality, but rather a design that keeps the framework's core simple while allowing extension as needed. Built on Werkzeug and Jinja2, it features high flexibility that lets developers choose only the necessary functions. It provides request/response processing through Werkzeug (WSGI utility library), a powerful template engine with Jinja2, and application modularization through blueprints. With a rich extension ecosystem including Flask-SQLAlchemy (ORM), Flask-Login (authentication), and Flask-Mail (email sending), it can handle everything from small prototypes to medium-scale web applications. It's adopted by companies like Pinterest, Netflix, LinkedIn, and Airbnb for microservices and API development, making it ideal for projects that prioritize rapid development and customization using Python's flexibility.

Merits and Demerits

Merits

  • Simple and lightweight: Minimal core functionality with low learning cost
  • High flexibility: Ability to select and add only necessary features
  • Rich extension ecosystem: Comprehensive extensions like Flask-SQLAlchemy, Flask-Login
  • Rapid prototyping: Efficient development of small-scale applications
  • Customizability: Easy integration with other libraries
  • Powerful templates: High-functionality template engine with Jinja2
  • Rich documentation: Detailed and clear official documentation

Demerits

  • Limited standard features: ORM, authentication, admin interface not included by default
  • Extension selection burden: Need to choose appropriate extensions from many options
  • Large-scale development limitations: Constraints at enterprise level development
  • Configuration complexity: Extension combinations can make configuration complex
  • Security: Security measures often left to developers

Main Links

Code Examples

Hello World

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

@app.route("/user/<name>")
def user(name):
    return f"<p>Hello, {name}!</p>"

if __name__ == "__main__":
    app.run(debug=True)

Templates and Routing

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html")

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]
        # Login processing
        return f"Welcome, {username}!"
    return render_template("login.html")

@app.route("/posts/<int:post_id>")
def show_post(post_id):
    # Retrieve post from database
    post = {"id": post_id, "title": "Sample Post", "content": "This is a sample post."}
    return render_template("post.html", post=post)

Modularization with Blueprints

# auth.py
from flask import Blueprint, render_template, request, session, redirect, url_for
from werkzeug.security import check_password_hash, generate_password_hash

auth = Blueprint('auth', __name__, url_prefix='/auth')

@auth.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        # User registration processing
        return redirect(url_for('auth.login'))
    return render_template('auth/register.html')

@auth.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        # Login processing
        session['user_id'] = username
        return redirect(url_for('index'))
    return render_template('auth/login.html')

# app.py
from flask import Flask
from auth import auth

app = Flask(__name__)
app.register_blueprint(auth)

Database Integration (Flask-SQLAlchemy)

from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text, nullable=False)
    date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)

@app.route("/")
def home():
    posts = Post.query.order_by(Post.date_posted.desc()).all()
    return render_template("home.html", posts=posts)

@app.route("/create", methods=['GET', 'POST'])
def create_post():
    if request.method == 'POST':
        post = Post(
            title=request.form['title'],
            content=request.form['content']
        )
        db.session.add(post)
        db.session.commit()
        return redirect(url_for('home'))
    return render_template("create_post.html")

if __name__ == "__main__":
    with app.app_context():
        db.create_all()
    app.run(debug=True)

API Development and JSON Response

from flask import Flask, jsonify, request
from functools import wraps

app = Flask(__name__)

# Dummy data
books = [
    {"id": 1, "title": "Python Guide", "author": "John Doe"},
    {"id": 2, "title": "Flask Practice", "author": "Jane Smith"}
]

def authenticate(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        token = request.headers.get('Authorization')
        if not token or token != 'Bearer your-api-token':
            return jsonify({'error': 'Unauthorized'}), 401
        return f(*args, **kwargs)
    return decorated_function

@app.route("/api/books", methods=["GET"])
@authenticate
def get_books():
    return jsonify({"books": books})

@app.route("/api/books", methods=["POST"])
@authenticate
def add_book():
    data = request.get_json()
    new_book = {
        "id": len(books) + 1,
        "title": data.get("title"),
        "author": data.get("author")
    }
    books.append(new_book)
    return jsonify({"book": new_book, "message": "Book created successfully"}), 201

@app.route("/api/books/<int:book_id>", methods=["GET"])
@authenticate
def get_book(book_id):
    book = next((book for book in books if book["id"] == book_id), None)
    if book:
        return jsonify({"book": book})
    return jsonify({"error": "Book not found"}), 404

Form Processing and Validation

from flask import Flask, render_template, request, flash, redirect, url_for
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField, PasswordField
from wtforms.validators import DataRequired, Length, Email, EqualTo

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'

class ContactForm(FlaskForm):
    name = StringField('Name', validators=[DataRequired(), Length(min=2, max=50)])
    email = StringField('Email', validators=[DataRequired(), Email()])
    message = TextAreaField('Message', validators=[DataRequired(), Length(min=10)])
    submit = SubmitField('Submit')

class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired(), Length(min=4, max=25)])
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired(), Length(min=8)])
    confirm_password = PasswordField('Confirm Password', 
                                   validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('Register')

@app.route("/contact", methods=['GET', 'POST'])
def contact():
    form = ContactForm()
    if form.validate_on_submit():
        name = form.name.data
        email = form.email.data
        message = form.message.data
        
        # Email sending or database save processing
        flash(f'Thank you {name}, your inquiry has been submitted!', 'success')
        return redirect(url_for('contact'))
    
    return render_template("contact.html", form=form)

@app.route("/register", methods=['GET', 'POST'])
def register():
    form = RegistrationForm()
    if form.validate_on_submit():
        # User registration processing
        flash('Registration completed!', 'success')
        return redirect(url_for('login'))
    return render_template("register.html", form=form)

Authentication and Session Management

from flask import Flask, render_template, request, session, redirect, url_for, flash
from werkzeug.security import generate_password_hash, check_password_hash
from functools import wraps

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'

# Dummy user database
users = {
    "admin": {
        "password": generate_password_hash("password123"),
        "email": "[email protected]"
    }
}

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'username' not in session:
            flash('Login required.', 'error')
            return redirect(url_for('login'))
        return f(*args, **kwargs)
    return decorated_function

@app.route("/login", methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        
        if username in users and check_password_hash(users[username]['password'], password):
            session['username'] = username
            flash('Login successful!', 'success')
            return redirect(url_for('dashboard'))
        else:
            flash('Incorrect username or password.', 'error')
    
    return render_template("login.html")

@app.route("/logout")
def logout():
    session.pop('username', None)
    flash('You have been logged out.', 'info')
    return redirect(url_for('login'))

@app.route("/dashboard")
@login_required
def dashboard():
    username = session['username']
    return render_template("dashboard.html", username=username)

@app.route("/profile")
@login_required
def profile():
    username = session['username']
    user_data = users.get(username, {})
    return render_template("profile.html", username=username, user_data=user_data)

Error Handling

from flask import Flask, render_template, jsonify, request
import logging
from datetime import datetime

app = Flask(__name__)

# Log configuration
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@app.errorhandler(404)
def not_found_error(error):
    return render_template('errors/404.html'), 404

@app.errorhandler(500)
def internal_error(error):
    logger.error(f'Server Error: {error}')
    return render_template('errors/500.html'), 500

@app.errorhandler(403)
def forbidden_error(error):
    return render_template('errors/403.html'), 403

# API error handling
@app.errorhandler(400)
def bad_request(error):
    if request.path.startswith('/api/'):
        return jsonify({'error': 'Bad Request', 'message': str(error)}), 400
    return render_template('errors/400.html'), 400

# Custom exception class
class ValidationError(Exception):
    def __init__(self, message, status_code=400):
        super().__init__()
        self.message = message
        self.status_code = status_code

@app.errorhandler(ValidationError)
def handle_validation_error(error):
    if request.path.startswith('/api/'):
        return jsonify({'error': 'Validation Error', 'message': error.message}), error.status_code
    flash(f'Error: {error.message}', 'error')
    return redirect(request.referrer or url_for('index'))

# API endpoint with logging
@app.route("/api/data/<int:data_id>")
def get_data(data_id):
    try:
        # Data retrieval processing
        if data_id <= 0:
            raise ValidationError("Invalid data ID")
        
        data = {"id": data_id, "name": f"Data {data_id}", "timestamp": datetime.now().isoformat()}
        logger.info(f'Data retrieved: {data_id}')
        return jsonify(data)
    
    except Exception as e:
        logger.error(f'Error retrieving data {data_id}: {str(e)}')
        raise

Testing

# test_app.py
import pytest
from flask import url_for
from app import app, users

@pytest.fixture
def client():
    app.config['TESTING'] = True
    app.config['WTF_CSRF_ENABLED'] = False
    with app.test_client() as client:
        with app.app_context():
            yield client

def test_home_page(client):
    response = client.get('/')
    assert response.status_code == 200
    assert b'Hello, World!' in response.data

def test_login_page(client):
    response = client.get('/login')
    assert response.status_code == 200

def test_login_success(client):
    response = client.post('/login', data={
        'username': 'admin',
        'password': 'password123'
    }, follow_redirects=True)
    assert response.status_code == 200
    assert b'dashboard' in response.data

def test_login_failure(client):
    response = client.post('/login', data={
        'username': 'admin',
        'password': 'wrongpassword'
    })
    assert response.status_code == 200
    assert b'Incorrect' in response.data

def test_protected_route_without_login(client):
    response = client.get('/dashboard')
    assert response.status_code == 302  # Redirect

def test_api_endpoint(client):
    response = client.get('/api/data/1')
    assert response.status_code == 200
    assert response.json['id'] == 1

def test_api_error_handling(client):
    response = client.get('/api/data/0')
    assert response.status_code == 400
    assert 'error' in response.json

# Test execution commands
# pip install pytest
# pytest
# pytest -v --cov=app