← All articles

6 min read

Why I chose Django over Flask and FastAPI for Scoreboards

Scoreboards tracks Cameroonian and African football: championships, teams, players, live matches. Why its backend runs on Django and not on Flask or FastAPI, what that choice gave me after two years, and what it cost.

  • Python
  • Django
  • Scoreboards

Scoreboards follows Cameroonian and African football: MTN Elite One and Elite Two, AFCON, WAFCON. Results, line-ups, goals, cards, standings, player pages, and live matches that update while they are played.

The backend is written in Python, and Python gives you three obvious choices for a web API: Flask, FastAPI and Django. I picked Django. This is why, and whether I would do it again.

What the backend actually has to do

Before comparing frameworks, it helps to be honest about the problem. A football data platform sounds like "an API that returns scores", but the backend today is:

  • 62 database models: championships, editions (seasons), teams, players, contracts, transfers, stadiums, referees, matches, goals, cards, substitutions, line-ups, suspensions, standings rules, votes, articles and more.
  • A REST API with 57 viewsets for the public site, the administration and a mobile app for the people who collect data at the stadium.
  • Accounts and permissions that depend on the data: a team manager can edit their team, a championship manager their championship, a reporter only the matches they are assigned to.
  • Live updates over websockets while a match is being played.
  • Background jobs: notifications, statistics, social media graphics.
  • A data import that loads JSON files from a separate data repository into the database.

Most of the work is not in the HTTP layer. It's in the data model, the relations between all those tables, and who is allowed to change what.

Flask: I would have rebuilt Django

I like Flask. It's small, readable, and perfect when you need five endpoints and nothing else.

But for Scoreboards I would have had to add, one by one: an ORM (SQLAlchemy), migrations (Alembic), authentication, permissions, an admin interface, serializers and validation, API documentation, a task queue and websockets. Each of those is a good library on its own. Together they are a framework I would have assembled, wired and maintained myself, without the documentation and the thousands of projects that already run Django's version of the same thing.

For a project I build alone next to a full-time job, that is time not spent on football data.

FastAPI: great, but not where my bottleneck is

FastAPI was the serious alternative. It's fast, async from the start, and the type hints give you validation and OpenAPI docs almost for free. For a small service, or an API in front of a machine learning model, it's what I would reach for.

What made me choose Django anyway:

  • The data model is the product. FastAPI leaves the database to you. With 62 related models, I wanted one ORM, one migration system and one way to write queries, all designed together.
  • Speed was not the problem. A Scoreboards page is slow when the database query is slow, not when the framework is. The fixes that mattered were select_related, prefetch_related and indexes, and those depend on the ORM, not on async.
  • Live matches still work. Django runs on ASGI. With Django Channels and Redis, the match page receives goals and cards over a websocket as they happen. I didn't need FastAPI to get real-time updates.
  • Batteries I would have to rebuild, again: admin, auth, permissions, management commands.

What Django gave me

Looking back after two years, these are the parts I would not want to give up.

Migrations. The schema has gone through 130 migrations. When I moved production from SQLite to PostgreSQL, the schema came along with the migrations. The tests now run on both databases in CI, and I never had to maintain two sets of SQL.

The admin. Before the custom administration app existed, the Django admin was the administration. I registered the models and had a working back office on day one to enter teams, players and matches. It's still there today for the occasional fix.

Django REST Framework. Viewsets, serializers, filters and pagination cover most endpoints with very little code. With drf-spectacular, the OpenAPI schema and the Swagger UI are generated from the code, so the API documentation can't drift away from what the API really does.

One project, many apps. The first plan for Scoreboards was microservices talking over Kafka. What I built instead is one Django project with one app per domain: matches, teams, championships, referees, predictions, notifications, blog. The boundaries are still there, but when a match result is saved in matches, a Django signal rebuilds the standings in championships right away, without a message broker in between. For a one-person project, that was the right call.

Tests. The backend has 783 tests, written with Django's test runner. The test client, fixtures and a test database per run come with the framework, so writing a test for a new endpoint is never a project of its own.

Management commands. The data import is a manage.py sync_data command. It runs with the full ORM and settings, from the terminal, from a cron job or in CI.

What it cost

Django is not free:

  • It's heavy. For a tiny service, a Django project has a lot of structure you don't need.
  • Async is partial. The ORM can be called from async code, but most of Django is still written for synchronous requests. Websockets go through Channels, which is one more piece to understand.
  • DRF can get slow on large nested serializers. A few endpoints needed hand-tuned queries and lighter serializers.
  • "The Django way" is sometimes not your way, and fighting it costs more than following it.

None of these hurt Scoreboards enough to regret the choice.

How I decide now

My rule today is simple:

  • A few endpoints, no real data model: Flask, or FastAPI.
  • An API in front of a model or another service, where async and speed matter: FastAPI.
  • A product with users, permissions, a real data model and an admin: Django.

Scoreboards is clearly the third kind, and so are the other products I'm building right now. They run on Django too.

Need a Python backend?

Everything in this article is work I also do for clients. If you are starting a web product, or your current backend has grown faster than its structure, I can help with:

  • Choosing the stack: Django, FastAPI or Flask, based on your product and your team, not on trends.
  • Building the backend: data model, REST API, authentication and permissions, background jobs, real-time updates.
  • Getting it to production: Docker, CI, PostgreSQL, and hosting on Azure or your own servers.
  • Reviewing what you have: slow queries, a schema that fights you, or code nobody wants to touch anymore.

Tell me about your project in a few lines. I read every message myself and reply with an honest opinion, even if the answer is that you don't need me.

And if you follow Cameroonian football, have a look at Scoreboards.