Combining Django ORM & FastAPI in a Single App

This video features Mia Bajić at DjangoCon Europe 2024 in Vigo, Spain.

Combining Django ORM & FastAPI in a Single App
0:26:30
Published July 11, 2024
1,372 views

Talk: Combining Django ORM & FastAPI in a Single App by Mia Bajić

https://pretalx.evolutio.pt/djangocon-europe-2024/talk/NGXABE/

Summary

Mia Bajić explains why one application might combine FastAPI with Django ORM, drawing on experience maintaining such a codebase. FastAPI offers asynchronous handling for I/O-heavy workloads, automatic OpenAPI documentation, dependency injection, and flexible structure, while Django ORM provides a high-level database API, migrations, and—most importantly for her team—the Django admin. The combination also brings costs: synchronous/asynchronous boundaries, incomplete async support in Django, incompatibility with some Django and testing tools, limited community guidance, and concerns about FastAPI’s project governance. She outlines the configuration, endpoint, deployment, and testing patterns needed to make the setup work, and concludes that for a new project she would first consider Django Ninja, FastAPI with SQLAlchemy, or a full Django application depending on requirements.

Key takeaways

  • FastAPI is useful for I/O-heavy applications because of its asynchronous capabilities, while it also supplies automatic API documentation and dependency overrides for testing.
  • Django ORM remains attractive for its high-level interface, straightforward migrations, and especially its built-in admin panel for non-programmer users.
  • Combining the frameworks requires careful handling of synchronous and asynchronous code, and Django still lacks async support for some important features such as transactions.
  • The unconventional setup can prevent Django ecosystem tools from working out of the box and leaves fewer online examples and troubleshooting resources.
  • Mia recommends evaluating Django Ninja, FastAPI with SQLAlchemy, or full Django for a new project, while treating an existing mixed codebase as an opportunity to understand and configure both frameworks deeply.

Summarised automatically from the transcript.

Chapters

  1. 0:00 Introduction and Technology Background Mia introduces her experience with Flask and Django before framing the unconventional FastAPI and Django ORM combination.
  2. 1:33 FastAPI Architecture and Basics An overview of FastAPI, its Starlette and Pydantic foundations, and simple endpoint and validation examples.
  3. 3:12 Performance and FastAPI Use Cases The talk examines FastAPI’s performance, asynchronous capabilities, and suitability for I/O-heavy applications.
  4. 4:44 Documentation and Dependency Injection Mia covers built-in API documentation and explains how dependency injection simplifies testing and code organization.
  5. 8:45 Django Admin and Framework Flexibility A story about customizing Django Admin illustrates the tradeoff between Django’s conventions and FastAPI’s flexibility.
  6. 11:04 Why Use Django ORM with FastAPI The benefits of Django ORM, especially its Admin panel, migrations, and higher-level abstractions, are compared with common alternatives.
  7. 13:21 Synchronous and Asynchronous Code Mia describes the complexity of combining FastAPI’s asynchronous model with Django’s synchronous features and the limits of Django’s async support.
  8. 14:57 Ecosystem and Community Limitations The talk explores compatibility issues with testing tools, the scarcity of guidance for unconventional setups, and FastAPI’s governance concerns.
  9. 17:30 Framework Integration Setup A practical walkthrough covers configuring Django, initializing FastAPI, using Docker, and connecting the application to the database.
  10. 22:12 Unit and Integration Testing Mia demonstrates endpoint setup, database fixtures, unit tests with fake repositories, and database-backed integration tests.
  11. 24:32 Project Structure and Recommendations The talk compares Django’s opinionated structure with modular FastAPI projects and concludes with guidance on choosing or combining the frameworks.

Transcript

4,303 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:00

Hello everyone, I am Mia, and today I'm here to tell you an intriguing story about working with a code base which has blended two quite different technologies together. I'll share my personal insights on how to navigate through it, highlight The advantages, struggles, and lessons learned along the way. The first framework I have an experience working with was Flask. I use it mostly for my hobby projects, but also for my diploma thesis. What I like about Flask is that it's very lightweight, very simple, and very easy to use. Then I began working for a company that used Django. We were creating typical crude apps for our internal employees and Django was great.

0:47

It had all the things we needed, it worked quite well, out of the box, it did not require any configuration, and moreover, there are lots of different tools in very rich Django ecosystems that we were using. And finally, I joined my current company where we use fast ABI, but in one unconventional setup that I want to talk about today. Fast API with Django ORM. If we have a look at the current state of Python ecosystem, uh Django is the most popular framework. It leads the pack, it's used by forty percent of responders, followed closely by Flask 38%, and Fast API 25%. Data from 2023.

1:33

However, the interesting fact about FastAPI is its huge growth and massive adoption, rising from 14% in 2021 to 25 % in 2023. What is FastAPI? What is this new framework some people are talking about? It's a newer framework released in December 2018. It's modern and it's flexible. It's built on the shoulders of two giants, Starlet and Pydentic. Pydentic is a Python library used for data validation, serialization, and documentation, and Starlet is a web toolkit. What it means basically is that you can take any of its components and you can use them independently. Starlet uses UVCorn and it implements

2:20

Asgis spec. Now the question is how can we start with Fast API? So let's see some examples. This is the simplest example you can you can find. So first we create an instance of the FAS API class, which will be our main application. Then we define a route and finally we create the endpoint that just returns message hello world. Now let's see how FastAPI leverages PyDentic for data validation. First we create a base model class called item with some specific attributes and we define here their types. Then we create an endpoint that uses our Pydantic class as the response module. So what happens here basically is that if the data doesn't match the specified types, then an error message is raised

3:12

Let's see some example about database interaction. So first we define a function in our crew. py file that basically just returns all objects of a model called item. Then on the API layer we can call our accrued method, passing the inject database section and other query parameters. Okay, so now some of you might be wondering, but why? Why would you use Fast API and for example not use Django or uh Django Rest framework? Um so My experience is that most people and organizations use it because it's performant. And it's performant largely due to its asynchronous capabilities.

3:57

Here we can see some benchmarks comparing different Python. frameworks where we measure responses per second with 20 queries per request. You can visit the specified URL and you can check it on your own. You can tweak configuration and see how does it work with different databases, for example, different amount of requests. But basically fast API consistently ranks as one of the fastest Python frameworks here on the fifth place Above it is Starlet and UVCorn that it's built on. Then there is Routerling, which is a framework I've never heard of and it's not very famous. And uh after it we can see some more frameworks, and then we can see Django here on the The 14th uh place. And in one of the codebases that I am

4:44

supporting, the reason why Fast API was initially chosen was its performance. is because we create a um data visualization application and that application fetches data from multiple uh endpoints so there are a lot of IO bound tasks and uh performance was uh the most important um requirement. Another thing which is very nice about Fast API, this is not a reason why people mostly use it, but it's a nice plus, is that it offers documentation of endpoints. So So if you open your app and you uh go to slash docs, you can explore and interact with your API directly Alternatively, if you use a

5:29

uh if you prefer a different style, you can go to slash redoc and you can see the documentation in redoc format, which also utilizes the OpenAPI specification. This feature is it's possible to have it in Django, but you have to use third-party um libraries. It's not built in like in fast API. Uh there 's one more thing about FAST API that I really like and that's dependency injections. So um I read everywhere that dependency injections are really useful and that they're very nice that help can keep your c code clean. But I struggled a lot with understanding how exactly. I understand the broad concept, but I really For me it was hard to find a place in my code base to say, okay, this is the line where I really see the difference it makes.

6:18

So one day I was writing tests And first I had to write a test for a function that fetches data from some ext external endpoints, it does something with the data, there's some business logic. So I started thinking how to do it. So the first thing you have to do is you have to mock your external responses because uh it was a unit test and I didn't want to call the external endpoints. So I started first I had to figure out where everywhere do I call external endpoints and when you have a huge enterprise application you have lots of layers, lots of different files. And then I also had to figure out what kind of uh response do I need there. And it was a very painful process. Like who likes smoking? Because you have to figure out where everywhere you're calling these things

7:05

and what uh what is your response. And then uh I wrote the test and then I had to write a test for another function, but this one was using dependency injection. And that's why I realized why dependency injections are useful. So let me show you an example. So here, let's say we have a service that fetches data from some external production API. And here's an example how you can use it. And here we have an endpoint that utilizes this service. So basically, each time the endpoint slash fetch data is called, an External call is made. Now, how can we test it without calling the external endpoint in a very simple and easy way? So here's an example how our test might look like First, we create an async mock object that has a return value, and if we want to swap our dependencies and use the async mock instead of the production service, we can call dependency overrides.

7:59

And then we proceed to test it as usual. So basically in just one line we manage to swap our service with another. Another nice thing about dependency injection is that you can see them all at once. You can see what are all other services that your service depends on. And basically it creates a very nice graph. So even before you start writing tests, you know how many dependencies. dependencies you have and you can prepare your test data. And I think this is a very nice pattern. I'm not seeing it used with Django, but I think it really helps you to keep your code clean and it really helps you with writing tests. Another thing about Fast API is its simplicity. I've worked for Django for a long time

8:45

and whenever I used admin panel, it was mostly just to update data, to delete something, or something similar. I always use admin as is. And admin is great because you can just interact with your models there basically. So it was great. One day I was tasked with tweaking the admin panel. We had to add a button which unclicks invokes some custom script. Now I'm not a front-end developer, but I'm sure I'm pretty I'm able to write a function that would uh invoke a custom script and uh but and create a button within fifteen minutes because it's a very common thing, right? Like buttons which do something on click are everywhere. So I thought okay, this will be a very easy thing to do.

9:32

So I started figuring out how does Django admin work. And to my surprise, I somehow thought it would be like front-end where you have different components and you see there all the properties and everything, so it's very easy to tweak anything. But then I realized That's not how it works. In order how does it work, you have to go to the documentation, read about it, you have to go lots of layers under it, check the source code, and that's because in Django there are lots of things happening under the hood. And I think that's great. That's great if you want to use the things as they are. But as long as you have to tweak things, it's very hard to figure out what's happening under the hood. So basically I spent a whole day figuring out how to add that one button. When I added it I realized that it's there

10:19

but under another button that was already there. So I had to change its properties so it's moves somewhere else. And I also wanted it in a different color. And after it I just decided to change my approach and do it in a uh different way, to invoke the script in a different way. And uh I think I said Uh it's Django is great when you use things as they are, but sometimes you need to tweak things a lot and I think uh frameworks like Fast API can be a bit better because they're a bit more flexible, so you can customize them as much as you want. So now you might be wondering: so if you decide to use Fast API for any reasons or for projects that already use FastAPI, uh what ORM should you use?

11:04

Is Django RM commonly used with FastAPI? If you have a look at the most popular Fast API open source projects on GitHub, like this one for example, you'll notice that none of them use Django RM. Most of these, in fact, use SQL Alchemy. So now you might be wondering why? Why do we use Django or app with Fast API or what benefits can it bring? One of the killer features of Django that I really like is its admin panel that I already mentioned. I think it's super useful when you have users who are, for example, not programmers. So for example, you have support engineers or you have consultants and they need to prepare some data,

11:50

they need to change some data, delete something, update. And sometimes you don't want to give them an access to your database. Or maybe no they are not technical enough and they don't know how to work with the database. So Django admin is really great. And in the code base that I am uh working with, this was one of the biggest reasons why Django RM was chosen because of its admin panel. Django Fast API doesn't have any built-in Django panel uh built-in panel when you use it with SQL Alchemy for example. I know there are some third party libraries, but uh even these third party libraries are not as good as uh Django admin And the second reason why uh lots of people prefer Django RM is because it's very simple, it's very easy to use I f my first RM

12:35

was SQL Alchemy and I switched to Django from it and to me Django feels like it's more high level and because there are less things you have to take care of. For example, session management. In SQL Alchemy you have to do it by yourself. And I also have with alambic and jjango migrations and I just find Django migrations very simple, very easy to use and everything always works. So While these two frameworks have some benefits, there's a reason why it's not commonly used. So let's explore some of the challenges it presents. The first thing you notice when you start working with a code base that mixes synchronous and asynchronous code is how often this error message happens.

13:21

And it happens even in places you didn't really know they exist in your database. How many of you have ever encountered this error message? Raise your hand Okay, I see around ten people, so not so many people. Um if you decide to use both, fast API which is asynchronous and Django which is synchronous, then you're almost guaranteed to uh run into it sooner and later. And that's because managing synchronous and asynchronous code adds another layer of complexity that you have to take care of. Let me share a quick story called There and Back Again. When I joined our team a year and a half ago, our code base was sync first. This meant everything was synchronous except of API routes because they were fast API, which is asynchronous, and we were

14:08

Using their uh sync to async decorators. Last year in April 2023, Django has released lots of asynchronous features. So the idea was now when we can have everything asynchronous. Let's just experiment and try to rewrite the codebase and see if an async first approach makes sense. So my colleague started rewriting the codebase and the change initially seemed successful. Everything worked fine. However, the devil is in the details. While Django has released lots of new async features, there are still lots of features that are not there. For example, async transactions. And for this for us this was extremely important because we have lots of endpoints where we call uh different operations and it's and it's important that they're run in a transaction.

14:57

So for this reason everything had to be reverted back to sync first approach. One of the great things about Django is it's a very rich ecosystem. So there are lots of different libraries which we can which you can use, which is great so you don't have to reinvent the wheel and create them yourself. However, if you use fast API with Django ORM, even though you use Django or M, you still cannot use lots of these libraries And that's because Fast API works in a bit different way. So for example, I tried to use uh PyTest Django and XGist to speed up tests and they don't work And that's because Fast API uses Starless Test client, which is a bit different. Some tools do work, but you have to tweak them a bit, meaning they don't work out of the box.

15:49

When you encounter a problem, what is the first thing you typically do? You Google it. You check Stack Overflow, Google, tutorials, documentation. There are lots of different resources on the internet, especially about junk God. There are Discord, uh servers, forums, documentation, everything. However, if you have any unconventional setup, if you use it with Fast API, you will not find anything on the internet. So basically you just have to experiment on your own. own. And the last thing I want to mention about about this combination, this is specifically about Fast API, and that is that there's only one person who has merge rights. And this is a problem for lots of uh uh companies and this is a problem for lots of people and a very common reason why they don't use fast

16:39

api in similar frameworks. So lots of people address this problem and the author replied to them If I suddenly died being hit by a bus, there are already mechanisms in place for code inheritance. There are others with GitHub rights to take over, etc. But I have requested to still review each PR myself. There have been Several cases where there's a PR with several approvals where no one really reviewed the code and it had a bug. Because of this, unfortunately, I still prefer to be quite strict about reviewing each PR that is merged. And I think this solution of others of having a process about code inheritance doesn't really solve the problem because if the author is not there then there would be another person who would be still the same person who has merge rights and when you're planning a project

17:30

You're not planning it for three months mostly, but you're planning it for years. So for you it's very important that the tools you are using have some stable Organization Bikani that there are multiple people who have the rights. How many of you do you know what is SwissGi's model? Raise your hand Okay, I see around 10 people, not so many people. This is a model which is often used in risk management and this is a model which is used in aviation. How do planes crash nowadays? When a plane crashes, it's never because of weather, because planes have been tested in all possible weather conditions. It's also not because of some hardware or software fail failure, because there are lots of mechanisms how to prevent these as well.

18:16

And also it's not because of human error, because it's something also that's been tested. But if an accident occurs It's because there are multiple factors that occur at once. So you can imagine it as each one of these being one layer. So in each layer you have some vulnerabilities. And in most cases it doesn't really matter. So you can have here so for example some User error but it doesn't matter because even if it goes to the next layer it will be stopped. But when there are multiple things that um Collide, then an accident happens. And this is how modern software works nowadays as well in big and major companies. When there's some huge outer, some big issue, it's never because of one isolating thing that happened, but it's mostly because of multiple things.

19:02

So my way of thinking about this problem is not like what if the author gets hit by bus? But what if the author gets bit hit by a bus and for example there is a security vulnerability that needs to be patched immediately? I don't know what is the answer, but I know that this is a problem for lots of organizations that would like to use uh similar frameworks and I also know that lots of organizations up organizations appreciate about Django having a stable core maintainers and also having a foundation that's standing behind it The last point I would like to touch on is the initial setup. If you use Django, you can use commands like start projects and you're ready to begin. Similarly with Fast API, you just need a few lines and you're ready to go.

19:49

But if you have any custom setup, then it would take you way more time to set up everything. So How can we set it up? First, let's have a look at the diagram. So basically we have a FastAPI application and it utilizes Django or M for database communication. Where do we start? We are integrating two different frameworks, so we must configure them separately. First we begin with Django and the database layer. First, we have our settings. py file, which is used in Django. I've changed here only the database name. Everything else remains unchanged Next, we need a whisky. py file to import Django settings model and launch the op uh application.

20:38

Here we set up the Django settings module called Django Setup and populate the apps with our installed apps. And now Django is ready. The second part involves setting a fast API. So we start by creating a fast API app instance, then we define routes and connect them. For deployment, I prefer using Docker. Here is a Docker file example. Here we first specify our base image and working directory, copy our requirements Install them, copy all necessary files, expose port 8000, and finally we launch the app using Uvicorn. So if we open local calls and type in slash docs, we can see the app.

21:25

Moving on, how do we create an endpoint that lists some data stored in a database? First, we define our endpoint path as some description and response model using our PyDenti model. Then we make a database call which is wrapped in the sync to async decorator. It's a coroutine, so we must await it. The fetch data is then serialized as a piedantic model. For Django versions above above 4. 2, you can use AGET directly without the decorator. The implementation basically manages the synchronous and say asynchronous code under the hood so you don't have to take care of it. However, keep in mind that while Django has added lots of asing features, not everything has been added yet

22:12

And if we call the endpoint, we can see there's some data return. Now let's move on to tests The print simple is basically the same as for setting our main app. First we need to set up Django. First we define our Django settings module, then we populate apps, and finally run migrations before each session. Because we want the database to be clear for each run, we can create a fixture that truncates all tables after each test run And then we move on to the second part of our setup, which is the fast API side. We create a fixture where basically we just import the app as is. And the last part involves setting up a client to which we pass our important uh uh imported app and configure its base

22:57

URL So let's see how we can create a test. Our test will evaluate the creation of a model. Let's start with unit test. First we create some fake repository with some fake data and it might look something like this. So basically instead of persisting data into a database, we store it in an array which we can access later We start with creating a request with some data, then we call our book service, but instead of using its normal repository we use the fake one. Next we call the method and pass the request data and after that we can access the array where the data is stored. Finally, we can assert whether the stored data equals the data from the request.

23:44

How about integration tests? If you want to test all of your endpoint and you want to use a database. So you could write something like this. First, you need some database fixtures that will take care of truncating tables, setting up the books app and client fixture. Then we call the endpoint slash books and we assert that the response code is 200. We can also define the expected data and then compare the expected data with the received one. And that's how you could set up test. So we've seen how it might be implemented, but you're now probably wondering how about the right structure? If you start off with Django, there is a structure that's already set up for you.

24:32

And that's why that's because Django is quite opinionated. It was created to promote specific approaches to web development. Fast API on the other hand is very flexible, so you can use it as you want. When it comes to bigger projects, I personally linked towards a module based structure. It's predictable and extensible. And it's used, for example, in also very big and famous projects like Netflix project dispatch on GitHub. And this structure works pretty well even if you Use Django or M. Alright, so we've you we've discussed some benefits and some challenges of this setup and we've discussed how we can implement it, but the question is

25:18

Should we use it together? I personally love working with both frameworks But if I were to start a new project from scratch, I would first check Django Ninja as it kind of gives you the best of both words. Alternatively, I would also tag uh fast API with SQL Alchemy or Full Django with Jugger or M depending on project requirements. However, it might happen that you end up on a project which is already integrated to different frameworks together. In that case I encourage you to experiment because when things don't work out of the box you have to tweak things a lot and configure and for that you have to learn how does it work out under the hood.

26:03

So it's great fun and also it's a great learning process. And at the end, I would like to uh mention what my esteemed colleague said when he joined our team during our first retrospective meeting. It's crazy, but it works. I'm Mia Baij, thank you for your attention.

Questions this talk answers

What is FastAPI, and what is it built on?

FastAPI is a modern, flexible Python framework released in 2018. It builds on Starlette for web functionality and Pydantic for data validation, serialization, and documentation.

Discussed at 1:33

Why use FastAPI instead of Django or Django REST Framework?

The main reason is performance, especially for applications with many I/O-bound tasks, because FastAPI supports asynchronous execution. It also provides interactive OpenAPI documentation and built-in dependency injection.

Discussed at 3:12

How does dependency injection make FastAPI code easier to test?

FastAPI lets you replace a production dependency with an async mock through dependency overrides. This avoids calling external services during tests and can replace the dependency in a single line.

Discussed at 7:05

Why combine FastAPI with the Django ORM?

The combination provides Django’s useful admin panel and its high-level ORM, simple session management, and migrations while retaining FastAPI’s API performance and flexibility. In the speaker’s codebase, the admin panel was a major reason for choosing Django ORM.

Discussed at 11:04

What problems can arise when combining FastAPI and Django ORM?

The combination mixes FastAPI’s asynchronous code with Django’s traditionally synchronous code, creating complexity and frequent sync/async errors. It can also make Django ecosystem tools harder to use, provide little documentation for the unconventional setup, and limit access to some newer async features such as transactions.

Discussed at 12:35

How do you set up FastAPI with the Django ORM?

Configure Django separately with its settings, installed apps, and a setup module that initializes Django, then create the FastAPI application and routes separately. The application can be deployed with Uvicorn, commonly in Docker, and the API accesses Django models through the configured Django environment.

Discussed at 19:49

How do you create a FastAPI endpoint that reads data through Django ORM?

Define the route and its Pydantic response model, then call the database operation through a sync-to-async wrapper and await it. On Django versions above 4.2, supported asynchronous ORM methods such as `aget` can be used directly.

Discussed at 21:25

How do you test a FastAPI application that uses Django ORM?

Set up Django settings and apps, run migrations, and use fixtures to clear the database between tests; then create a FastAPI client fixture. Unit tests can substitute fake repositories, while integration tests call endpoints against the test database and compare the response with expected data.

Discussed at 22:12

What project structure works well for a larger FastAPI and Django ORM application?

The speaker favors a module-based structure because it is predictable and extensible. Unlike Django’s more opinionated default structure, FastAPI allows this organization to be adapted to the project.

Discussed at 24:32

Should you use FastAPI and Django ORM together in a new project?

The speaker would first consider Django Ninja, or choose FastAPI with SQLAlchemy or full Django with Django ORM depending on the requirements. The combined approach can still be worthwhile for an existing project, especially as a learning and experimentation opportunity.

Discussed at 25:18

Presenters

Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.

More videos by Mia Bajić

More videos from DjangoCon Europe