Cheatsheet: Flask vs Django vs FastAPI¶
Side-by-side comparisons of common tasks across all three frameworks. Use this as a quick reference while working on exercises or projects.
Table of Contents¶
- Hello World
- Route with URL Parameter
- Reading Request Body (JSON)
- Returning JSON
- Middleware / Request Hooks
- Database Model Definition
- Database Query
- Authentication Check
- Configuration
- Running the Dev Server
- Running in Production
- Testing an Endpoint
Hello World¶
Flask
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Hello, World!"
if __name__ == "__main__":
app.run(debug=True)
Django
# urls.py
from django.urls import path
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, World!")
urlpatterns = [
path("", index),
]
FastAPI
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def index():
return {"message": "Hello, World!"}
Route with URL Parameter¶
Flask
Django
# urls.py
path("users/<int:user_id>/", views.get_user),
# views.py
def get_user(request, user_id):
return JsonResponse({"id": user_id})
FastAPI
@app.get("/users/{user_id}")
def get_user(user_id: int): # type annotation = automatic validation
return {"id": user_id}
Reading Request Body (JSON)¶
Flask
from flask import request
@app.route("/users", methods=["POST"])
def create_user():
data = request.get_json() # dict or None if body is not JSON
name = data["name"]
return {"created": name}, 201
Django (DRF)
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(["POST"])
def create_user(request):
name = request.data["name"] # DRF parses JSON automatically
return Response({"created": name}, status=201)
FastAPI
from pydantic import BaseModel
class UserCreate(BaseModel):
name: str
email: str
@app.post("/users", status_code=201)
def create_user(body: UserCreate): # Pydantic validates; 422 on bad input
return {"created": body.name}
Returning JSON¶
Flask
from flask import jsonify
@app.route("/data")
def data():
return jsonify({"key": "value"})
# OR in Flask 2.2+:
return {"key": "value"} # Flask auto-serializes dicts
Django
FastAPI
Middleware / Request Hooks¶
Flask
@app.before_request
def before():
# Runs before every request
pass
@app.after_request
def after(response):
response.headers["X-Custom"] = "value"
return response
Django
# middleware.py
class CustomMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Code before the view
response = self.get_response(request)
# Code after the view
response["X-Custom"] = "value"
return response
# settings.py
MIDDLEWARE = ["myapp.middleware.CustomMiddleware", ...]
FastAPI
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
class CustomMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
response = await call_next(request)
response.headers["X-Custom"] = "value"
return response
app.add_middleware(CustomMiddleware)
Database Model Definition¶
Flask (Flask-SQLAlchemy)
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128), nullable=False)
email = db.Column(db.String(256), unique=True, nullable=False)
Django
from django.db import models
class User(models.Model):
name = models.CharField(max_length=128)
email = models.EmailField(unique=True)
class Meta:
db_table = "users"
FastAPI (SQLAlchemy 2.0)
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(nullable=False)
email: Mapped[str] = mapped_column(unique=True, nullable=False)
Database Query¶
Flask (Flask-SQLAlchemy)
# Get one by primary key
user = db.session.get(User, user_id)
# Filter
users = User.query.filter_by(active=True).all()
# SQLAlchemy 2.0 style (preferred)
from sqlalchemy import select
stmt = select(User).where(User.active == True)
users = db.session.execute(stmt).scalars().all()
Django
# Get one
user = User.objects.get(pk=user_id) # raises DoesNotExist if not found
# Filter
users = User.objects.filter(active=True) # lazy QuerySet
# Get or 404
from django.shortcuts import get_object_or_404
user = get_object_or_404(User, pk=user_id)
FastAPI (SQLAlchemy 2.0 async)
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
async def get_user(db: AsyncSession, user_id: int):
result = await db.execute(select(User).where(User.id == user_id))
return result.scalar_one_or_none()
Authentication Check¶
Flask (Flask-Login)
from flask_login import login_required, current_user
@app.route("/protected")
@login_required
def protected():
return f"Hello, {current_user.name}"
Django
from django.contrib.auth.decorators import login_required
@login_required
def protected(request):
return HttpResponse(f"Hello, {request.user.username}")
FastAPI (JWT via Depends)
from fastapi import Depends
from .auth import get_current_user
@app.get("/protected")
def protected(user = Depends(get_current_user)):
return {"hello": user.name}
Configuration¶
Flask
# Using a config class
class Config:
DEBUG = False
SQLALCHEMY_DATABASE_URI = os.environ["DATABASE_URL"]
app.config.from_object(Config)
Django
# settings.py — a Python module, import env vars with os.environ or django-environ
import os
DEBUG = os.environ.get("DEBUG", "False") == "True"
DATABASES = {
"default": {"ENGINE": "django.db.backends.postgresql", "NAME": os.environ["DB_NAME"]}
}
FastAPI (Pydantic Settings)
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
secret_key: str
debug: bool = False
class Config:
env_file = ".env"
settings = Settings()
Running the Dev Server¶
Flask
Django
FastAPI
uvicorn myapp.main:app --reload
# With host and port:
uvicorn myapp.main:app --reload --host 0.0.0.0 --port 8000
Running in Production¶
Flask
Django
FastAPI
uvicorn myapp.main:app --workers 4 --host 0.0.0.0 --port 8000
# Or with gunicorn + uvicorn workers:
gunicorn myapp.main:app -k uvicorn.workers.UvicornWorker --workers 4 --bind 0.0.0.0:8000
Testing an Endpoint¶
Flask
import pytest
from myapp import create_app
@pytest.fixture
def client():
app = create_app({"TESTING": True, "SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:"})
with app.test_client() as client:
yield client
def test_index(client):
response = client.get("/")
assert response.status_code == 200
Django
from django.test import TestCase, Client
class IndexViewTest(TestCase):
def setUp(self):
self.client = Client()
def test_index(self):
response = self.client.get("/")
self.assertEqual(response.status_code, 200)
FastAPI