PythonMastery
intermediate 18 min read · lesson 12 of 12 in Web Frameworks

Which Framework, When

1 · The lesson

read

You've now built apps in all three of Python's major web frameworks — Flask, FastAPI, and Django. The question every developer eventually faces is which one for the next thing? The internet is full of strong opinions, most of them written by people who only know one of the three.

This lesson is the honest cross-framework comparison — the trade-offs, a decision tree you can actually use, and the cases where each framework genuinely wins. No verdicts about which is "best" overall, because that question doesn't have an answer without knowing what you're building.

Run locally — no code in this lesson; it's a decision-making lesson. The 🎯 Your Turn section asks you to apply the framework to five realistic briefs.


1. The Honest Comparison Table

DimensionFlaskFastAPIDjango
StyleMinimal, you assembleModern, async-firstBatteries-included
Best forCustom apps, learning the HTTP layerAPI-only services, async I/OContent sites, admin-heavy, complete apps
Learning curveLowLow–medium (Pydantic + async)Medium–high (the Django way)
AsyncBolted on (2.x supports async def views)NativeBolted on (async views since 3.1, async ORM 4.1+, still maturing)
ORMBring your own (SQLAlchemy usually)Bring your own (SQLAlchemy/SQLModel)Django ORM (excellent, deeply integrated)
Admin UINoneNoneBuilt-in (the legendary admin)
AuthBring your own (Flask-Login + Flask-Bcrypt)Bring your ownBuilt-in (django.contrib.auth)
API docsBring your own (apispec, flasgger)Auto (OpenAPI + Swagger UI from type hints)DRF + drf-spectacular
TemplatingJinja2 (default)Jinja2 (optional)Django templates (built-in)
Forms + CSRFBring your own (Flask-WTF)Bring your ownBuilt-in
MigrationsAdd AlembicAdd AlembicBuilt-in (makemigrations)
Real-time perf (RPS)MediumHighest (uvicorn ASGI)Lowest (sync WSGI by default)
Community sizeMassive, matureFast-growing, very activeMassive, oldest of the three
Job market (2026)Many roles, often legacyHighest growth, dominant on new API rolesSteady demand, especially enterprise + content

Two things to read from this table:

Django has the most pre-built features. Auth, admin, ORM, migrations, forms, CSRF, sessions — all bundled. For a "ship an MVP solo" or "internal tool with logins" project, you can be 60% done before you'd have finished setting up the equivalent Flask stack.

FastAPI has the best performance ceiling and the best API ergonomics. Native async, automatic OpenAPI, Pydantic validation. For a JSON API consumed by an SPA or mobile app, it's the modern default.

Flask has the lowest cognitive overhead and the most freedom. Small core, no opinions. If you want to understand every line of your stack, or you're maintaining a service that's already in Flask, it's still excellent.


2. The Decision Tree

Walk down it. Stop at the first one that fires.

  • Do you need an admin UI for non-technical users to manage content/data?
→ Django. Nothing else comes close. Building this from scratch in Flask or FastAPI is weeks of work; in Django it's admin.site.register(Model).
  • Are you building a backend API for an SPA, mobile app, or other API client, with no server-rendered pages?
→ FastAPI. (Or Django + DRF if you have other Django reasons — e.g. you already have a Django monolith and want one stack.)
  • Do you want to learn how web frameworks work under the hood, or teach someone HTTP fundamentals?
→ Flask. It's small enough that you can fit the whole framework in your head; you'll see every layer (request → routing → view → response).
  • Is this a high-concurrency I/O-bound service — many DB queries, external API calls, websockets, streaming?
→ FastAPI on uvicorn. Native async is a 5–10× throughput win over sync WSGI on this workload.
  • Content-heavy site with CMS-like patterns, marketing pages, blog posts, structured editorial content?
→ Django (or Wagtail, which is Django-based and built specifically for this).
  • Are you stuck on a Flask codebase you can't replace?
→ Stay with Flask. The ecosystem (Flask-Login, Flask-WTF, Flask-Migrate, Flask-RESTful, Flask-SQLAlchemy) is mature and covers everything Django does, just à la carte.
  • Microservices — many small services, each owning one domain, deployed independently?
→ FastAPI per service. Lean footprint, fast startup, async by default. Django is overkill for a service that owns three endpoints.
  • Solo founder shipping an MVP fast, full-stack, everything included?
→ Django. The admin alone saves you a week. Auth saves another week. You can always swap pieces later.
  • Mostly server-rendered HTML with a few sprinkle-of-React interactions (HTMX, Alpine, Stimulus)?
→ Django or Flask. FastAPI is solvable but the template story is a side-show — Django's {% extends %} + admin pairs naturally.
  • You already have a strong opinion about all three?
→ Use the one your team knows best. Framework velocity is dominated by familiarity. A Django expert ships faster in Django than in FastAPI even on a pure API project.

3. The Real-World Picture in 2026

What's actually being built and maintained right now:

  • FastAPI is dominant for new API-first products. It's the default choice for backend roles at AI startups, fintechs, and any "we have a React frontend and a Python backend" shop founded after 2022. It runs the inference servers for half the open-source AI tooling.
  • Django is dominant for content sites, internal tools, marketplaces, and Python monoliths. The admin keeps it irreplaceable for any product where non-engineers manage data. Instagram, Pinterest, Mozilla, Disqus, Eventbrite — all Django at significant scale.
  • Flask is the smaller-share-but-still-huge tier. Legacy systems that worked fine and never got rewritten, internal tools at engineering-heavy orgs, "I want full control" greenfield projects, and a large educational footprint because it's the easiest to teach. Netflix, Reddit (parts of), and Lyft have all used Flask in production.

None of them are dying. None of them are unambiguously "the best". The Python web ecosystem is in the rare healthy state of having three excellent options that overlap less than they appear to.


4. What's Actually Different — and What Isn't

Most of the things that feel like framework choices aren't:

  • All three can serve HTML, return JSON, talk to Postgres, scale to 100K requests/minute behind a load balancer, deploy on Kubernetes, run on Lambda, talk to Redis, and authenticate users. Capability is rarely the constraint.
  • All three have great test ecosystems. pytest works with all of them.
  • All three have first-class typing support (Flask via stubs, Django via django-stubs, FastAPI natively).
  • All three deploy the same way in 2026 — uvicorn/gunicorn behind nginx, or a managed PaaS, or a serverless function.

What's genuinely different:

  • How much you write yourself versus how much comes pre-decided. Django decides; Flask doesn't.
  • Async story. FastAPI is native; Django and Flask added it.
  • Built-in admin. Django has it; the other two don't.
  • Validation ergonomics. FastAPI's Pydantic + type-hints pipeline is a step ahead.

Once you know that, "which framework" turns from a philosophical question into a small set of practical trade-offs.


5. What This Lesson Is Not

This is not a verdict that one framework is "better". Anyone who tells you "always use X" without asking what you're building is wrong, full stop. The same engineer can — and probably should — use different frameworks for different problems.

In particular, three claims that show up in framework debates online and are wrong:

  • "Django is too heavy / old." Django is a complete framework. The weight is value you'd otherwise build yourself. "Old" in software ages is "battle-tested and stable" — Instagram serves a billion users on Django.
  • "FastAPI is just hype / not production-ready." FastAPI has been in production at Uber, Netflix, Microsoft, and most AI startups for years. It is production-ready.
  • "Flask is dead." Flask 3.x exists, is actively maintained, and runs at scale at large companies. It's smaller than it once was relative to FastAPI, but "small" in the Python ecosystem is still enormous.

The framework affects velocity and ergonomics, not capability. Choose the one that makes your problem easier today, knowing you can migrate pieces later if needed.


6. The Migration Question

A common worry: "what if I pick wrong and have to migrate?" Three honest observations:

1. Migrations between Python frameworks are rare in practice. Most apps that succeed stay on their original framework forever. The cost of rewriting is almost always higher than the cost of living with a slightly imperfect choice.
2. When migrations do happen, they're incremental. You typically don't rewrite — you put the new framework alongside the old, behind the same load balancer, with new endpoints on the new stack. Both run for years.
3. The portable pieces are most of the code. Your business logic, your models (especially if you use SQLAlchemy across all three), your tests, your queries — these don't change. What changes is the routing layer, the view functions, and the serialisation layer. Significant work, but bounded.

So: pick by today's fit. Don't agonise about lock-in. The good code you write is portable; the framework is the thinnest layer of the stack.


7. Common Mistakes

1. Framework cargo-culting. "Use FastAPI because it's trendy / Hacker News says so" when Django would ship the MVP in a third the time. The framework that's right for a 100-engineer team building a high-RPS microservice is not necessarily right for a solo founder shipping a CRUD app.

2. Resisting Django ORM because it's "not SQLAlchemy". The Django ORM is excellent. It has fewer escape hatches than SQLAlchemy and a less expressive query builder for very complex cases, but for 95% of CRUD work it's simpler and more productive. Use the framework's defaults unless you have a concrete reason not to.

3. Building a CRUD admin in Flask instead of using Django. Three weeks of building Flask admin pages with Flask-Admin + custom permissions could have been one afternoon with admin.site.register(Model). If your project needs an admin, that pushes the needle hard toward Django.

4. Using Django for a single API endpoint. Standing up a Django project for one webhook receiver is overkill. A 20-line FastAPI service or a 15-line Flask app does the job and deploys in a Lambda.

5. Picking by "performance benchmarks" alone. Hello-world benchmark RPS rarely predicts real-world performance. Database, network, and serialisation dominate. The framework whose ergonomics let you ship the right architecture matters far more than the framework whose hello-world is 30% faster.

6. Refusing to learn the other two. Even if you only use one professionally, knowing the others gives you better instincts about what your framework is doing (and what good defaults look like). A senior Python engineer should be able to read code in all three.


🎯 Your Turn — Pick the Right Framework

Below are five realistic project briefs. For each one, pick Flask, FastAPI, or Django (or Django+DRF) and write one sentence justifying the choice. There's room for debate on some — what matters is that your reasoning is grounded in actual trade-offs, not vibes.

text
Brief 1 — INTERNAL ADMIN TOOL
A 50-person company needs an internal tool to manage customer records,
invoices, and support tickets. Five engineers will build it; the users
are mostly non-technical (sales, support, finance). It needs auth,
permissions, basic dashboards, and full CRUD on every table.

Brief 2 — REAL-TIME CHAT SERVICE
A chat backend with websockets, presence, typing indicators, and 50K
concurrent connections. Stateless API otherwise; data lives in Redis
and Postgres. One frontend team (React + iOS + Android) consumes it.

Brief 3 — MARKETING WEBSITE WITH NEWSLETTER SIGNUP
A small marketing site for a startup — about page, blog, contact form,
newsletter signup. Content updated weekly by the marketing team
(non-technical). SEO matters; performance matters. No app, no users
beyond the marketing team's CMS access.

Brief 4 — ML INFERENCE API
A FastAPI-or-not decision. A single endpoint that loads an ML model,
accepts JSON input, runs inference, and returns JSON. Will need to
scale to 1000 RPS. Async-friendly because the model call is GPU-bound.
No frontend, no auth (internal network only).

Brief 5 — LEGACY SAAS WITH 200K LINES OF FLASK
An existing 200K-LOC Flask 2.x codebase that ships a B2B SaaS app.
Stable revenue, no plans to rewrite. You're joining the team and
the product roadmap is steady feature delivery. You're asked whether
to "modernise" by rewriting in Django or FastAPI.
Hint 1 — Match the bullet, not the brand Re-read the decision tree in Section 2. For each brief, find the bullet that fires first. Brief 1's "admin UI for non-technical users" is a hard match for Django. Brief 2's "websockets + high concurrency" pushes hard toward FastAPI. Don't over-think; the bullets are designed to give you a clear first signal.
Hint 2 — The legacy brief is a trap Brief 5 isn't really a framework-choice question — it's a "should we rewrite?" question. The answer is almost never yes. Frame your justification around the migration cost vs the marginal benefit, not around which framework you'd pick on a greenfield.
Show full solution

These are the canonical answers. Reasonable people can argue some edges — but each has a clear best fit.

Brief 1 — INTERNAL ADMIN TOOL → Django

The admin UI alone makes this a non-decision. admin.site.register(Customer), admin.site.register(Invoice), admin.site.register(Ticket), customise list_display / list_filter / search_fields, and you have 70% of the product. Auth is built in. Permissions via Django groups. Migrations free. Five engineers shipping in Django are at least twice as productive on this problem as five engineers shipping in Flask or FastAPI, because they're not building any of the foundational pieces.

Brief 2 — REAL-TIME CHAT SERVICE → FastAPI

Websockets, 50K concurrent connections, async-everything — this is exactly what FastAPI + uvicorn is built for. Django Channels can do websockets too, but the rest of the brief (stateless API, separate frontend teams, no admin needed) doesn't benefit from Django's batteries. FastAPI lets you write async def everywhere, scales horizontally cleanly, and the OpenAPI docs help the three frontend teams stay in sync on the schema.

Brief 3 — MARKETING WEBSITE WITH NEWSLETTER SIGNUP → Django (or Wagtail on Django)

A weekly-updated content site with non-technical editors is the Django sweet spot. The admin is where marketing edits the blog. Django's templating handles the rendered HTML. If the content modelling gets richer (structured pages, navigation menus, asset library), upgrade to Wagtail (a CMS built on Django) — same underlying framework, much more CMS power. Flask works too, but you'll spend the first week building "admin for marketing to edit blog posts" that Django gives you for free.

Brief 4 — ML INFERENCE API → FastAPI

Async (because GPU inference releases the GIL and you can serve other requests during the call), high RPS target, single JSON endpoint, no admin, no auth. Pydantic for the request/response schema documents the API for the internal callers. This is the FastAPI canonical case. Django for one endpoint would be silly; Flask is fine but FastAPI's async + auto-OpenAPI is strictly better here.

Brief 5 — LEGACY 200K-LOC FLASK → Stay on Flask

Rewriting 200K lines of working code in another framework is a multi-year project that delivers no new customer value. The Flask ecosystem (Flask-Login, Flask-SQLAlchemy, Flask-Migrate, Flask-RESTful) covers everything Django and FastAPI cover. Modernise within Flask if anything — upgrade to Flask 3, adopt async views where it helps, swap in pydantic for request validation if it'd be nice, layer FastAPI alongside for any new services that benefit from async, but do not rewrite. The product is shipping; engineering velocity is the constraint, and rewrites destroy velocity for a year minimum.

The meta-lesson: framework choice is contextual. The same engineer should answer "Django" to brief 1 and "FastAPI" to brief 2 on the same day without contradicting themselves. Anyone who'd answer the same framework for all five briefs is missing what each framework is actually good at.


What You Learned

  • There is no universally-best Python web framework. Flask, FastAPI, and Django each win different cases. Pick by problem, not by tribe.
  • Django wins when you need an admin UI, you're building content-heavy or full-stack apps, or you want batteries-included productivity for an MVP.
  • FastAPI wins on API-only services, async-heavy workloads, microservices, and modern Pydantic-first development.
  • Flask wins when you want minimalism, full control, are teaching the HTTP layer, or are maintaining an existing Flask codebase.
  • Most "framework debates" are wrong because they treat the choice as universal. It's contextual.
  • Capability is rarely the constraint — all three can do almost anything. Velocity and ergonomics on your specific problem are what differ.
  • Don't agonise about lock-in. Migrations are rare, incremental, and the portable code (business logic, models, tests) survives.
  • Knowing all three makes you a better engineer in whichever you primarily use — it gives you instincts about what good defaults look like and what your framework is doing under the covers.

Next: with the framework choice behind you, the production concerns are the same across all three — see deployment, Auth & JWT, Postgres for production, and Docker for Python for what comes after "I picked a framework".