Flask
The Python micro framework for building web applications - lightweight and flexible. Simple yet extensible design for prototyping to production.
GitHub Overview
pallets/flask
The Python micro framework for building web applications.
Topics
Star History
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
- Flask Official Site
- Flask Official Documentation
- Flask GitHub Repository
- Flask Tutorial
- Flask Extension Libraries
- Awesome Flask
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