Django vs. FastAPI, An Honest Comparison

photo

David Dahan

Dec 2024 12 min
tech
web
work
python
django
fastapi
image presentation

After many years of working with Django, I decided to take a closer and more serious look at FastAPI.
Extensively using both frameworks has helped me understand their actual strengths and weaknesses with greater accuracy.
More and more, I find it challenging to form a relevant opinion on tools I've never used. Reading documentation or blog posts like this one is not enough, as some things simply don't "click" in your mind until you experience them firsthand.

To be clear, this blog post is not about declaring a winner—it’s not a fight. However, the frameworks are so different that, depending on your project requirements, one will likely be a better fit than the other.

The challenge when comparing tools is distinguishing when one is genuinely better than the other and when it’s simply a matter of personal preference that others might not share. You might strongly disagree with some of my points, and that’s completely understandable in this context.

Batteries or not?

Django is marketed as an all-batteries-included framework. This means it is designed to provide many of the tools you might need, all built-in and without relying on external dependencies.

Generally speaking, I appreciate this approach because it’s often more efficient: you don’t need to search for and compare countless packages, you rely on a single documentation source, the built-in tools are designed to work seamlessly together, and there’s less ambiguity about how to achieve the same tasks. Since these tools are part of the same package, you don’t have to worry about version conflicts—for example, an update to package A breaking compatibility with package B.

Some of Django's well-known "batteries" include its ORM (more on that later), data validation, authentication, session management, caching, command-line tools, and its automatic CRUD admin interface. The latter stands out compared to tools like pgAdmin, as any write operation respects validation rules, ensuring database integrity. This feature has saved me from potential issues multiple times.

While the "all-batteries-included" philosophy was true in 2010, making Django a genuine full-stack framework, it’s more debatable today. Many features you might need are not included in Django itself—ironically, even a REST API package isn’t part of the core framework. When starting a "serious" Django project, I usually add 10 to 15 additional packages. This might sound excessive, but each added package feels like a departure from the original philosophy, leaving me thinking, "This feature would be better if it were built directly into the framework.". That said, I realize I’m wrong to think this way because Django is designed from the ground up to be extensible, with a robust plugin system. Adding external packages is not only acceptable but often encouraged.

In FastAPI, there are fewer batteries included within the framework itself. For example, for data validation, you use Pydantic, and for the ORM, you typically rely on SQLAlchemy. Some of these external tools are mandatory, some are recommended as examples, and for less obvious use cases, you’ll need to find your own solutions.

Using external tools has clear advantages (cf. lock-in section), but it usually involves a bit more hassle during setup. You’ll need to handle the "plumbing," connecting the pieces together. If you’re not familiar with this process, you might have to look for examples to see how others approach it, as not everything is thoroughly documented.

Moreover, with FastAPI, things can become frustrating when you need a basic feature that isn’t available. For instance, if you require pagination (which is common when building an API), you essentially have two options:

  1. Find and integrate an external package: While this can work, it requires time to assess the package: Does it function as expected? Is it production-ready? How good is the documentation? Is the community active enough to help if you encounter issues? Can you trust that it will be maintained in the long run? Although similar challenges can occur with Django, they happen A) less frequently and B) involve packages that are usually more mature, benefiting from years of maintenance.
  2. Build it yourself: This can be a great learning experience and allows you to tailor the solution to your specific needs, but it’s far from efficient. The frustration of having to reinvent the wheel when all you want is a ready-to-use solution can be discouraging.

FastAPI does not include a plugin system, which makes external integrations a bit less streamlined.

That said, if having full control over your code is a top priority, FastAPI (or any low-level framework like Flask) combined with a "do it yourself" approach will likely be your preferred choice.

To conclude this section, I’d say I’m not entirely satisfied with either framework in this regard. But stay tuned — the conclusion will introduce a third framework that might just hit the sweet spot.

Architecture

Django not only provides batteries but also enforces a structure to follow. It adheres to the MVT (Model-View-Template) structure, which is quite similar to the well-known MVC architecture. It comes with guidelines, conventions, and best practices to help you stay on track.

Here’s Django speaking to you: "Trust me, let me handle the complex decisions for you. I’ll show you how to organize your project, the best practices to follow, and everything will work like a charm." And it’s true—Django works. It’s a solid and reliable framework.

But what if your project doesn’t fit the "Django way"? Remember, Django was released in 2005, and nearly 20 years ago, this MVC-like approach to building websites was considered state-of-the-art. Things have evolved since then, and there are now many ways to create websites, such as SPAs, microservices, serverless architectures, JAMstack, and more.

Additionally, some projects have specific requirements for performance, latency, resource optimization, or real-time capabilities. There are also fields like AI, IoT, or other specialized domains where a monolithic architecture is not the best fit.

As a general rule, high-level frameworks like Django excel when they align with your needs but can become a real headache when you try to work against them. That’s why you should never choose a framework randomly, especially high-level ones. It’s essential to understand the specific requirements of your project before selecting a framework and to anticipate the implications of your choice.

The more you find yourself thinking, "This is the traditional Django way, but I need to do it differently," throughout your project, the more likely it is that you should consider avoiding Django and opting for something like FastAPI instead.

FastAPI is far less opinionated when it comes to architecture, as it doesn’t enforce any specific structure. It’s more like, "Here are some essential tools to give you a strong foundation—now go ahead and build the rest however you see fit."

You might genuinely wonder: why not just use FastAPI if it can fit all my needs instead of Django? The answer is that if a high-level tool like Django perfectly matches your requirements, using a lower-level tool like FastAPI might take significantly more effort and time to achieve the same results.

Back-end vs. Full-stack

Django is a full-stack framework (front-end + back-end), whereas FastAPI focuses solely on the back-end with the primary goal of building APIs. However, Django's front-end capabilities are limited to creating "old-fashioned" Multi-Page Applications (MPAs). To put it simply, this means that clicking on a link refreshes the entire page. This contrasts with Single Page Applications (SPAs), where sites are highly interactive and only load new data from the back-end without refreshing the page.

Both approaches are perfectly valid. However, if you choose to build an SPA (using frameworks like React, Vue, or Angular), Django's front-end becomes irrelevant. While this isn’t an issue at all, I personally find it "dirty" to deal with bloated source code or documentation full of front-end features I’ll never use.

That said, I particularly appreciate Django’s front-end capabilities when building admin dashboards, where CRUD operations are frequent but UI interactivity or advanced UX isn’t a primary concern. In such cases, it’s more efficient than using an API.

Lock-in

More dedicated batteries often mean a greater risk of lock-in. For example, when you learn Django ORM, it’s only usable within Django projects. On the other hand, when you work with FastAPI, you need to learn related tools like Pydantic or SQLAlchemy.

The advantage of mastering these individual tools is their versatility. They are specialized in what they do and can be applied across various contexts. It’s like being a mason equipped with cutting-edge, highly specialized tools—you can choose to use them or not, depending on the project.

Tools like Pydantic, for instance, can be utilized outside of web-first projects. A great example is in the field of data science. Imagine a scenario where a back-end developer and a data scientist are working on the same project. Even though they focus on different aspects, they could still share Pydantic models. This increases the overlap and collaboration between teams.

It’s a bit abstract, but working with Django often makes you feel like a "Django developer," while working with FastAPI makes you feel more like a "Python developer." This is because the web aspect of FastAPI feels more "bounded".

Easiness / learning curve

Django is beginner-friendly. It often provides a recommended way to do things, and tutorials are well-crafted. Many of the "recipes" you need are demonstrated somewhere on the internet. Django has also had relatively few breaking changes over time, so even older Stack Overflow answers are often still valid.

FastAPI (and other low-level frameworks) is not as beginner-friendly because you need to make many choices yourself, which increases the risk of doing things the wrong way. However, the documentation helps guide you through basic needs. The more advanced your requirements become, the more you need to understand what you’re doing, which is not necessarily a bad thing.

I personally dislike marketing claims that emphasize how easy it is to write a "Hello World" in under 10 lines. Your project won’t be a "Hello World," so these examples are irrelevant to me. While Django requires more boilerplate, that doesn’t mean it’s harder to build a project with it.

With Django, much of the complexity is hidden from you. You’re not forced to understand how things work behind the scenes to get your site running. This allows beginners to achieve working solutions more quickly. However, you should be cautious of the "it just works" feeling, because when something doesn’t work, debugging can be harder since you might not know what’s happening behind the scenes. My advice: read the Django source code alongside the documentation, not just the quickstart part.

FastAPI, on the other hand, has less built into the framework itself, but because you make choices and implement things yourself, you’re forced to understand how they work. This takes more time but forces to undestand what you're doing. And your code essentially becomes your documentation.

But using conventions and pre-defined architecture, like Django’s, can also teach you how to build a well-structured project. By using Django, you’re learning a proven path that works.

Modernity

I’m not sure this should be a standalone criterion, but let me share my frustration with Django in this area.

Django hasn’t evolved much in recent years. Some argue this is a good thing for backward compatibility and robustness. However, my intuition is that Django has become too large and old to remain agile.

A glaring example is that the framework is still not fully asynchronous, even though work on this began over six years ago. Another significant shortcoming, especially in 2025, is the lack of official type support. I’m a huge fan of strictly typed codebases—they act as documentation, prevent countless bugs before runtime, and are an obvious choice for me. While there are unofficial Django stubs, I’m not a fan of these "band-aid" solutions.

On the other hand, FastAPI excels in this regard. It integrates seamlessly with Pydantic, which leverages Python type annotations. This modern approach provides features like auto-complete in your code editor and enforces type safety. Additionally, WebSockets are easy to implement in FastAPI thanks to its built-in async support.

In my opinion, Django could have evolved more effectively over the years, following the example of frameworks like Rails or Laravel, which have integrated modern features to stay relevant across a wider range of use cases.

Performance

I’m not a huge fan of comparing the raw performance of languages or frameworks, because, in my experience, bottlenecks are more often caused by human mistakes—like poorly written queries with excessive repetition or long-running tasks that should have been delegated to a background worker.

That said, I might be biased, as I’ve mostly worked on small to medium-sized projects where minor performance gains have little impact.

For what it’s worth, FastAPI’s default serialization performance is likely better, thanks to Pydantic v2, and it makes async usage more straightforward. However, you can still use async in Django (with more effort) and opt for a custom serializer.

Ultimately, if raw performance is a top priority, I’d question whether using a Python framework at all is the right choice.

ORM

I won’t dive into all the features of both frameworks, but the ORM is a critical topic, especially since it seems to spark polarized opinions. Over the years, I’ve heard extreme comments like "SQLAlchemy is the devil" or "Django isn’t a real ORM."

In my experience, the Django ORM is very simple to use, gets straight to the point, and has an intuitive syntax. However, it’s far removed from raw SQL. When using it, it’s pretty clear that you’re not learning SQL itself—you’re learning Django’s way of writing queries. This can become problematic when you need to craft very complex queries, as you might have to resort to raw SQL, which can feel cumbersome if you’re not familiar with it.

SQLAlchemy, on the other hand, aims to mimic SQL closely. If you already know SQL well, writing queries in SQLAlchemy will feel natural. It’s a more powerful and permissive tool, offering lots of possibilities. But with great power comes great responsibility—using SQLAlchemy effectively requires a solid understanding of both SQL and the library itself.

Both ORMs are fundamentally different, and that’s for valid reasons. They’re both excellent tools, but Django’s ORM seels more beginner-friendly to me.

Governance

I’m not a fan of single-author frameworks. It’s like a Single Point Of Failure, which introduces several long-term risks. Here are a few examples:

The author makes a bad decision

For instance, the creator of FastAPI decided to introduce SQLModel, an abstraction over both Pydantic and SQLAlchemy. The goal is to use a single model for both data validation and database table definitions, promoting DRY principles. While the intention is good, the approach has proven very controversial and has led to fragmentation within the framework.

The framework’s popularity grows, and the author can’t keep up with PRs and issues

This actually happened with FastAPI a few years ago. There was even a GitHub issue discussing the difficulty of managing the growing workload.

By contrast, Django has a more democratic governance model, with periodic board elections to guide its future. While I’m deeply grateful to the FastAPI author for their immense contributions to the open-source world, it doesn’t erase the fragility inherent in relying on a single individual.

Configuration

With FastAPI, you need to configure many things yourself. A striking example for me was setting up unit tests. In Django, you simply run the test command—a test database is automatically created, the tests are executed, and that’s it.

In FastAPI, you need to build this mechanism yourself. While it may seem straightforward, it’s actually quite nuanced. You need to figure out:

  • How to create a new database and reset it properly without risking damage to your real database.
  • How to drop data between tests while maintaining decent performance.
  • How to make your data factories aware of the test database. ...and so on.

Once again, the conclusion is clear: achieving your goals with FastAPI requires more initial work and skills. However, once it’s set up, you gain full control and a deep understanding of what’s happening behind the scenes. My surprise at needing to configure all this in FastAPI made me realize just how much Django handles automatically.

Django also offers additional tools to simplify configuration, such as built-in commands and fixtures to load a local database. In FastAPI, you have to handle these tasks manually or use a boilerplate project.

Conclusion

No surprise here, both frameworks have their strengths and weaknesses and operate quite differently. The pronounced differences between them are actually a good thing, as they can help you decide which one best suits your project’s needs. Don’t choose based on hype, choose based on your project requirements and the kind of skills you want to develop over time.

I hope this blog post has helped you make a wiser choice for your future project.

To conclude, I’ll add that there are other frameworks that might be even better suited to your needs. I’m currently keeping a close eye on a very promising framework called Litestar (formerly known as Starlite), which combines multiple strengths from both Django and FastAPI.