If you're a Django developer, I'm sure you've heard so many people raving about FastAPI and Pydantic.
But you really love Django, right? And you don't want to switch?
Well, then you might want to give Django Ninja a serious look.
Django Ninja is highly inspired by FastAPI, but also deeply integrated into Django itself.
We have Vitaly Kucherev, the creator of Django Ninja, on the show to tell us all about it.
This is Talk Python to Me, episode 490, recorded December 10th, 2024.
Are you ready for your host?
You're listening to Michael Kennedy on Talk Python to Me.
Live from Portland, Oregon, and this segment was made with Python.
Welcome to Talk Python to Me, a weekly podcast on Python.
This is your host, Michael Kennedy.
Follow me on Mastodon, where I'm @mkennedy, and follow the podcast using @talkpython,
both accounts over at fosstodon.org.
And keep up with the show and listen to over nine years of episodes at talkpython.fm.
If you want to be part of our live episodes, you can find the live streams over on YouTube.
Subscribe to our YouTube channel over at talkpython.fm/youtube and get notified about upcoming
shows.
This episode is brought to you by Sentry.
Don't let those errors go unnoticed.
Use Sentry like we do here at Talk Python.
Sign up at talkpython.fm/sentry.
And this episode is brought to you by Bluehost.
Do you need a website fast?
Get Bluehost.
Their AI builds your WordPress site in minutes, and their built-in tools optimize your growth.
Don't wait.
Visit talkpython.fm/bluehost to get started.
Hey, everyone.
Before we jump into the interview, I want to tell you about a massive article I wrote I
think you'll find interesting.
I did data science things and dug into all the episodes this year and pulled out the top
five Talk Python to me episodes of 2024.
Then I did a deep dive into the content, what was covered, what was important, even learned
a few things that I missed the first time around.
Just visit talkpython.fm/blog, and the article is right at the top of the page.
I'll put the full link in the podcast show notes.
Also, if you haven't discovered it yet, Talk Python has a blog.
While you're there, please subscribe to the RSS feed to get all the articles and announcements.
Thanks for listening.
Happy holidays, everyone.
Vitaly, welcome to Talk Python to me.
Yep.
Hello, hello.
Yeah, thank you for having me.
This is going to be the first time I've ever had a ninja on the show.
Okay.
That will be fun.
No, Django Ninja.
Django Ninja.
It's going to be a lot of fun, isn't it?
Yep.
That's right.
Yeah.
So welcome to the show.
We're going to talk about Django Ninja, which is, at least in my mind, you tell me if I'm
seeing this correctly as we get into it.
But in my mind, I see Django Ninja as Django's FastAPI.
That's exactly like the most asked questions I ever had.
Like, why not just FastAPI?
So I hope I will answer during this podcast why it is, why it exists.
And we will all work with this question.
Ever since it first came out, I was, I saw him.
That's cool.
That is a cool idea.
And I'm pretty excited to talk to you about it.
It's going to be great.
Before we get into all that, though, quick introduction.
Who are you?
What do you do?
How do you get into this Python world?
Yeah.
So my name is Vitaly Kucherawe.
Yeah.
Good luck.
Good luck repeating that last name for everyone.
But yeah, that's my name.
So I'm from Ukraine.
I'm currently in Ukraine.
And I'm in tech for like, I don't know, like more than 20 years probably.
And yeah, I worked with, well, at the beginning of my career, I worked with all sorts of languages,
technologies, like everything, like C++, .NET, Java, PHP, like everything.
And yeah, I guess like one time I tried Python.
And yeah, since then, this is like my go-to tool.
And yeah, I just cannot switch to anything else.
Yeah.
That's how I got there.
And yeah, like currently I work for a company, Modicos Prime, where we do AI for big pharma companies.
We help them, you know, innovate in this area.
And yeah, there I also use Django Ninja.
And yeah, basically eating my own dog food.
So that's a good sign.
That's a very good sign.
And it's so excellent to be able to work on these projects, but also use them with the team.
And in reality, right, on a real day-to-day work, because I think that polishes a lot of the edges that might otherwise exist.
Basically, I'm watching like how other people use like in a team.
And also like I communicate with a bunch of companies who also move to Django Ninja and like are listening to what kind of struggles they have and which kind of problems they jump in.
So yeah, that kind of helps solve it easier and somewhat more maintainable way and easier to integrate.
So basically, that's my end goal.
Yeah, that's awesome.
There's a really interesting stat from the Python, the PSF and JetBrains survey.
I'll put it on the screen for people watching.
I think at least when I personally thought about sort of how popular is Django, I kind of always had it tied with Flask and close with FastAPI, right?
They're kind of, if you look at the survey results, they're kind of similar.
But what's interesting is if you actually look at what web developers use, Django is by far the most popular one, like way more popular than Flask.
50% more or something like that.
Not quite.
I think it's pretty important to have a library like this around to make Django first class citizen in this sort of data exchange typed Pydantic world, right?
This is pretty much described it.
I think like, yeah, before this survey, like there was like Flask and Django, like, you know, there was like a tie.
But now, like basically, I think FastAPI is eating a lot of this pie from Flask and Django.
And yeah, you see now it kind of gets into like, you know, three main players.
And yeah, like from what I experienced is I think it's actually like maybe developers are actually splitting into like two main groups.
Like ones who like to use some kind of tool that have everything like Django.
Yeah, like it can be in the Java world.
It can be like Spring framework in Python and Django.
While others like to, you know, start from scratch and like combine all little pieces together.
Yeah, like take their own RM, take their own configuration, take their own, you know, file system, like everything and make it all their own.
And this is where the market for Flask and FastAPI.
While for other folks, it's like more like, yeah, they just want something ready.
Like whatever issues it have, let's take it.
It's just, you know, more standardized way.
It kind of seems more maintainable for some people while others prefers more like, you know, replacing components, switching frameworks and do like everything custom.
So like my vision of Django is like it should be a little bit glued to two of these worlds.
Because what I noticed is like when people use Django Ninja, it's mainly like all the applications is actually like it's API.
There is no form.
There is no Django admin.
There is no like, you know, custom views.
You're like, so they basically tie to API and doing just the rest API.
And I think, yeah, this is basically when it shines and when you can take like an approach like for FastAPI Flask.
But under the hood, it will have all the batteries that comes with Django.
So I think that's how it's, why it's actually like picking up and why it works and why people use it.
I know there are a lot of people out there that have just some kind of JavaScript front end and a bunch of APIs.
But there's also a lot of websites that are more server side based, more holistic, kind of like Django does.
And having something like Django Ninja to sort of add on the API functionality when you've already got the models, you've already got everything set up.
That's a really cool aspect of it.
It's more interesting like in history, like why actually Django Ninja appear in Havio.
Yeah, yeah.
Back in the day.
So yeah, when Sebastian released FastAPI, I was like really like mind blown.
Like, this is like, this is all I wanted.
Like you just write a couple of types to your function and you got the UI for, you know, for just UI for API, documentation for API, validation and everything like in one package.
So yeah, like for lazy people like me, that was like, you know, like, yes, literally my mind blowing.
Yeah.
So basically I tried it out, kind of worked for me for like, you know, one project.
And that year, like for me, the case was like, I had to integrate it with Django.
So I had existing application with some database and stuff.
And I just needed like simple little API to add there.
So I decided, okay, I'll just take FastAPI for that.
So I took it, it worked, but the problem is like to FastAPI and the hood uses Starlet.
And there's like a little bit different approaches to like, like local storage and threads when you're basically accessing the database.
It's not like without like special hooks and crooks to like access like database within Starlette context.
And you can kind of lose this connection.
And then you get just, you know, error that you cannot really debug or do something with it.
So it was like, like for me, it was like, oh, come on.
Like it was so, so perfect, but it just breaks in the middle of something.
And yeah, but I tried like Django community.
They said, yeah, it's not, it's not, it's not something wrong.
FastAPI community.
They said, yeah, it's, it's not something that we do as well.
It's a Django problem.
It's a fastAPI problem.
Yeah.
So I, and then, then I just took a look inside and yeah, basically I said, okay, just this, but literally it's really simple.
Yeah.
You just take input from HTTP request, you apply by the intake models validation and yeah, and that's it.
And it works.
So like for me, the first prototype was like done in, in a weekend and it was already like working.
And then like rest of the time when I decided, okay, I'll try to make it a project, but yeah, to make it something that will pick up, I need to like, yeah, first make like, you know, 100% coverage and the documentation.
So that part is actually took me like a good amount of like, almost like, you know, half a month or months of work until like, it's like presentable.
So yeah, like the, yeah, the concept is like pretty, pretty easy, but the implementation and, you know, edge cases were like really the hardest part, but yeah, I just started, okay, I'll do it.
And yeah, that's, that's how, how it born.
And I guess the next part is like actually why it exists is like, yeah, so in PyDentic, there is model word is reserved for like, you know, any kind of validation.
Yeah.
So PyDentic uses word model for validation, but in Django world model means like tables, yeah, database tables.
So you cannot just bring PyDentic models.
It's the ORM, yeah.
Yeah.
And because people will be confused when you, okay, you speak about model and then you kind of need to think, okay, is it database or is schema?
So like for me, the main goal was to introduce class called schema, which is like a PyDentic model under the hood.
And then it should like hide as much as possible the word model.
So, and like to make it like as, as much as possible compatible with, with Django and just trying to hide the word model under the hood as much as possible.
And it kind of like, I'm lucky that schema kind of worked and yeah, because schema is, is actually how, like how Swagger defined.
Like open, open API standard names it.
So it kind of fits there perfectly.
And, and yeah, that's kind of how it works.
And yeah, then, then I, I was sitting like, it was, what should be the name of the thing?
Like it was also the hardest part.
So yeah, but yeah, I first tried it and it did like a really good performance in terms of like, you know, CPU usage and high loads.
It was like really lightweight.
So way faster than Django Ress framework.
So I thought, okay, she should be something fast.
Try it, you know, word here and there.
Okay.
And then, and it become ninja.
So that's how it's important.
The naming is perfect.
The naming is just perfect.
You and me both.
When I first saw FastAPI as well, I'm like, this is the way this is how it should be done.
And FastAPI is really interesting when it came on the scene and by way of being very similar.
So it was Django Ninja in that it, it was one of the first frameworks to say them and along with Pydantic say Python types can be something we use, not just something we run through a linter to make a better experience and the async aspect of it.
So, and I saw that like, yeah, this is the way you should build APIs.
And so when I saw Django Ninja, I'm like, of course Django should have this way.
This is the way.
The whole concept is perfect.
Yeah.
We just, you make a type hint and then it does validation for you.
The linting, it does the, you know, editor auto-completion, like everything in one package.
And yeah, like for, if you're lazy, you just thought I did one type in.
Like before that, I was like pretty skeptical, skeptical about typing.
Like, yeah, you just blather your code.
Doesn't, you know, like if you just lint in it with my pie, it's like really painful to make like fully tight project.
It's like, oh my God, it's like, it's like an X impossible to like satisfy my pie in every like, like, so Django Ninja is like my pie compatible.
But there is lots of stuff is just tight typing or it's just some stuff is just not possible or it's like too, too blathered with, with code.
So you have like, it's really hard to read when you fully type your code.
So even in my day, day to day projects, like what I basically try to do, I, I only do type annotation for arguments.
The rest is actually like editors do perfectly.
So if you return some object, the editor know, okay, this is like this type or that type.
And you don't have to like annotate what exactly does it return.
So a hundred percent.
That's exactly the way that I do it as well.
I know there's some projects whose goal is to basically make Python statically typed like Instagram, for example, to try to get rid of bugs and huge programs.
But for a lot of people, it sounds like you definitely for me, it's like I want the editor to work really well and know what it is that I can do here.
And if I put a type here and there, like on a return value of a function, all of a sudden it gets much easier to work with.
Yum.
This portion of Talk Python to me is brought to you by Sentry.
Code breaks.
It's a fact of life.
With Sentry, you can fix it faster.
As I've told you all before, we use Sentry on many of our apps and APIs here at Talk Python.
I recently used Sentry to help me track down one of the weirdest bugs I've run into in a long time.
Here's what happened.
When signing up for our mailing list, it would crash under a non-common execution pass, like situations where someone was already subscribed or entered an invalid email address or something like this.
The bizarre part was that our logging of that unusual condition itself was crashing.
How is it possible for our log to crash?
It's basically a glorified print statement.
Well, Sentry to the rescue.
I'm looking at the crash report right now, and I see way more information than you'd expect to find in any log statement.
And because it's production, debuggers are out of the question.
I see the traceback, of course, but also the browser version, client OS, server OS, server OS version, whether it's production or Q&A, the email and name of the person signing up.
That's the person who actually experienced the crash.
Dictionaries of data on the call stack and so much more.
What was the problem?
I initialized the logger with the string info for the level rather than the enumeration dot info, which was an integer based enum.
So the logging statement would crash, saying that I could not use less than or equal to between strings and ints.
Crazy town.
But with Sentry, I captured it, fixed it, and I even helped the user who experienced that crash.
Don't fly blind.
Fix code faster with Sentry.
Create your Sentry account now at talkpython.fm/sentry.
And if you sign up with the code talkpython, all capital, no spaces, it's good for two free months of Sentry's business plan, which will give you up to 20 times as many monthly events as well as other features.
All right, well, let's dive into Django Ninja.
I guess people got a pretty good sense that it's like FastAPI for Django.
If somebody asks you, what is Django Ninja?
How do you describe it?
What do you tell them?
So basically like extra battery for making.
So Django is all about batteries.
So it's that, I don't know, like it's more like, not a battery, but more like, you know, the energy pack.
If you need API and it's, you need to be, it's like pretty standard way.
Like it is currently, I think like open API standard is kind of one because before there was like here and there, like the different standards.
But yeah, like I think they kind of now become the defector and everything supports it.
Like, yeah, like the big players, like open AI supports open API.
So that kind of makes it easier to integrate with all sorts of tools.
And yeah, there's all different UIs.
Yeah.
Like how you can present your API.
So that kind of a big win.
And yeah, like then it's like deep Django integration because yeah, like in most of the cases you define your, like if you, let's say you want to return some stuff.
Yeah.
Like the list of some objects from your database.
Yeah.
You simply just return query set and then it's validated and described correctly in your documentation.
So yeah, like, I guess like this laziness in terms of that you don't have to write lots of stuff, make it like really perfect in terms of like, you know, day-to-day code.
Yeah.
Like, so you, you just, you, you just describe your, okay, this is a list of, you know, hero objects.
And then you return like queries and that's it.
So you don't have to very worry about like converting the data, serialization, deserialization.
It's like in just three, three lines of code, you usually be able to like, or some kind of filter and input and output some results from the database.
And this is, you know, 90% of business logic is usually like, okay, give me the JSON and return to JSON.
And then you just need some kind of easy glue in between.
I got to make one or two database queries and then here are the database objects, kind of.
One of the things when you're talking about building this, that seemed to me to be maybe the trickiest part of it would be the fact that you have to integrate it with the Django ORM, right?
When Sebastian built FastAPI, he could just go, well, whatever Pydantic does, I'm just going to do that.
And here we go.
Not to downplay the effort he's put into it.
I know it's a lot, but he didn't also have to say, and let's make that compatible with Django models, Django ORM objects, right?
So this integration that you put between those, so they're pretty seamless.
That's really cool.
Yeah.
This is basically like whenever I tried it, then it is something that you definitely need to like.
And it's not only like, because Django comes with like way, way, way more stuff.
And yeah, like because what Django Ninja comes included is like the caching.
Yeah.
That basically fully relies on Django, like a little bit of throttling, filtering, and you know, the fields integration.
Like, so you define some kind of columns on your database and that's automatically converted to your schema types.
It's like, you know, so you don't have to like repeat your work two times.
You're like define, you know, first database tables and then schema tables.
So I think this is like, again, kind of lazy approach, but it works and it's perfect.
This is really, really awesome.
I don't want to make this, I don't want to put you guys, your project in sort of competing against another project.
But for people who are using the Django REST framework, you know, why should they look at or what benefits do they get from Django Ninja?
It's more like, you know, a stealth player that, you know, takes majority.
Like, I think Django Ninja is still like, you know, was it like 5% or something like that on the latest polls?
But yeah, so it's, it's, it's really big industry and Django like REST framework has already like lots of plugins or like tools or like, you know, community that you can rely on.
But yeah, again, like the, the, basically, I think the problem that is solved with like this pidentic validation and serialization is like, yeah, you simply rely on like latest standards.
Yeah, like, so if you never use like type annotation, this is actually first, the good way to try it out and see the benefits.
Yeah.
Like, so you don't go like, you know, full 100% typed code, but you just lightly type it when you just need it for API.
And then you already can see, okay, what's, what's the benefits and how, how you actually can perform better in terms of like making less mistakes, like, you know, typos or like type conversions.
So like the main benefit is like, it was just, so you define the type input, like from some very complex object that your API inputs.
And then you have like runtime guarantee that whatever type you wrote there, you can just rely it.
You have guarantee that if you define it as an integer, then there will be like a string or I don't know, like a flaw there.
So you, you, you have this guarantee and then you, it kind of brings away from you this validation hassle.
So you can kind of be more relaxed in terms of, okay, yeah, my data is valid and I can work with those types without worrying about validation or, or serialization of the, of the, of those fields.
As you were describing that, I was thinking, think of all the value that Pydantic adds to working with JSON data.
You get that for your API at the boundary, right?
With no effort.
That's pretty amazing.
Yeah.
Pydantic is just, it's so good.
What about performance?
I know you have a, a graph, which I love it.
I never actually like remeasure it again, but that's how it worked at the beginning.
So I, I don't think like back in a day when this was introduced, I'm not sure if currently a FastAPI or I mean, Django REST framework have any like async support because back then it, it was not.
So you can, you can, you can, you could immediately have a boost on your performance.
If you just, you know, free from database.
So you can just return your, your query set as a sync.
And then you rely on, you know, event loop to pull those data and return it back to the clients.
And it was like really for, for some project that I've communicated with like other companies and they, they like, they saw like a performance, something like that.
Or like their CPU usage was like a, you know, load average about like 50% drop to like 5%.
Yeah.
So that was like really a immediate, immediate boost up, but it only works for you.
If you're like, yeah, for, for like very simple case when you just need to parse data, insert database, return, return the result.
So this is like where it's like performance just, you know, it can be like five to 10 times you can get this improvement.
Yeah.
And people want to see the graph.
They can just visit jingo-ninja.dev and it's right there on the homepage.
But yeah, if you start doing like you start, you know, doing some kind of square roots of some numbers, yeah, you will not get this performance.
You will have to, again, rely on number of processes or threads or stuff like that.
Did you do this analysis with Pydantic 1 or Pydantic 2?
This is Pydantic 1.
Yeah.
So I never actually measured like what is the performance.
So yeah, like, but again, it's like probably like you will see the difference with Pydantic 2 when you like really, so you're in, you have inputs of like really bulky Jasons.
Like, you know, let's say you have like, you know, payload that is like one megabyte and you will, you will definitely see performance.
But yeah, it was just small, small inputs.
Yeah.
It will not be that dramatic.
I see.
So if it's just a small input, then Pydantic is not the main part of what you're doing.
But if it becomes large, then the parsing becomes a big part of it.
Yeah.
There is also like one of the features of Django Ninja.
So you can replace your, your parsers and serializers.
So you can say, okay, you know what?
Let's use or Json for inputting out.
And again, you can, you can get some extra performance just because you have those parsers are quicker.
They have their own quirks.
Maybe not some, something like not standard, but for the, for the most cases, it just, you
can increase your performance with just, you know, adding a few configuration lines.
Yeah.
That's really interesting.
If you know that whatever difference those other faster parsers might have, it doesn't apply to the data you're exchanging.
Just use them.
Right.
Yeah.
What about UV loop for the async side of things?
The thing is like, it's really like Django Ninja has nothing to do with it.
It's fully relies on, on Django itself under the hood.
So like, yeah, basically Django starts, is GI process, do some stuff on top, you know, and then gives you basically back to Django, a final context with request.
And then you, you work on it.
It's probably down to the production app server that use like G UV a corn or hyper corn or, or whatever you run it on.
Right.
Like that thing you just specify that runs on UV loop and then off it goes.
Yeah.
I'm pretty interested to see what would this graph would look like with Pydantic two, because that's the one that was rewritten in rust, which I had Samuel Colvin on to talk about a time or two.
That's, that's fun.
So let's see, let's talk through, give us a sense of what programming this looks like.
And, you know, just keep in mind that, you know, not everyone can see what we're talking about, but give us as much as you can, sort of a, a feel for how to write code in Django Ninja.
You import Ninja API from Ninja, and this is your like main kind of API instance.
And so you, you create this instance, like API, your Ninja API, and then using the decorators, it was just API.get.post.put, like whatever you, method you want.
You just decorate some functions and then inside your functions, you are, you know, you do your business logic.
But the, the cool part is that you can define input parameters, which are basically gathered from your request.
So if you do define a couple variables type in, they will, by default, they will come from like query parameters.
If you define like more complex object, it will come from like your body and it will be parsed from, from JSON.
And yeah, like you define those functions, you include it into your URL patterns.
That's, that's it.
You have your, your ready API.
You don't have to like do like, put it into installed apps or like configure any settings.
So yeah, like my goal is also to create as simple as possible, like integration.
So just in two, three lines, you're ready, you have already API.
And yeah, that's, that's basically like did the whole, the whole, the whole thing.
That's it, right?
There's not a whole lot to it.
There's also like for, I guess for, for the difference for folks who work with FastAPI.
The key difference is that Jenga Ninja, Jenga Ninja's operation, their function, the first argument is always request.
Because this is like historically what Jenga, what Jenga protocol is.
Yeah.
Like your, your first argument to your view function is request.
And yeah, they, they rely a lot of stuff in their request can have user, you know, extra information like middle layers can access, you know, some kind of attributes.
Okay.
So this is like kind of important.
And this is like also like the, the key, the key difference or requests is request is always there as a first argument.
And yeah, like there was a suggestion, so let's remove it.
But I think, yeah, we should keep it, we should keep it consistent with, with, with default Jenga view.
And yeah, like, I think that, that, that, that works perfectly.
I don't remove it.
I like it.
I think, I know FastAPI is optional to have the request or not have the request, which, which is not a bad choice.
I just not too long ago converted the talk Python website into court, which is async flask with the goal of converting the course of websites and a bunch of other things that I have going over as well.
And in that world, you know, you just have this thread local request object.
And that always just seems like breaking the rules to me.
It's like, you know, what if I want to run this, another thread, how do I capture it and send it?
I just like having the object just passed in is nice.
I think I like it.
I think it's a clean design.
Yeah, it's like clear or like more open.
Yeah.
So you can always know, okay, this is something about, about the HTTP request.
So it's always there.
A little easier for testing and reasoning about it.
It's not like, well, if I call a function, which calls a function, which needs the request, is it safe for it to just grab the thread local request?
Or, you know, like there's a lot of ambiguity in here.
It's just like, yeah, here's the request object and go with it.
So that I'm, I'm, I'm a fan.
I'm a fan of it.
Another, another thing that's really nice, and you hinted at this before, is that it has automatic documentation, very, very similar to FastAPI for what you write without you doing any work, right?
You just go to slash API slash docs, and you get this nice UI, right?
You don't have any hassle of creating some UIs.
You just, you just have it for free, right?
So you just go to slash API slash docs, and there you have it.
And you can already like, so by default, Django Ninja comes with two, like three doc and a swagger.
But there is technically like pretty easy to integrate any other tool that can work with, with open API specification.
And yeah, that's, that's the way you can like fine tune or like make it nicer, easier, or like, you know, do some kind of custom stuff with your documentation when you share it back to like your, your clients, your users.
This is a very Django like feature as well.
I mean, I know it first probably was seen in FastAPI as an automatic thing, but you know, Django, you just create your models and you get the admin section and you get all these other things around what you've created.
And this is sort of, Hey, you just get this documentation and this testing tool for exploring your API just by creating it.
Basically it was Django who first sort of, yeah, we just give you admin for free, just define a couple of classes in there.
And I think like, I think the Django REST framework was also like the first who like gave you the UI also for free.
So you just define your serializers, you create your classes stuff, and you have UI that you can test.
So I think like the UI testing is like one of the most kind of best debugging tool you can, you can have.
Yeah.
So you like basically like I use it like day to day.
They are like define your function.
I just try it out on three UI and that's it.
That's like very, very, very helpful.
You can turn it off.
Right?
Yeah, sure.
It's really nice for, if you're making a public API, it's nice to just have it.
But if you're making an API that is internal, but your website has to be public, you might not want to publish the API just to the world if it's not meant to be a public API, right?
There is an option to define your custom authentication for the docs.
You can define, okay, docs, doc outs or something like that.
You can say, okay, only like, you know, Django super users can access this documentation.
So it's also like a pretty common request or feature that people use.
And as well, you can, yeah.
So I guess one of the interesting part that, that also differs from FastAPI.
So you can create like, you know, as, as, as many as possible ninja API instances.
So you can have like slash public API slash private slash custom slash, yeah, whatever, like version one, version two, like, and basically you can define multiple, multiple versions, reuse functions.
And then, you know, can continue your work while keeping the old, old, you know, version one, version two code or private public, stuff like that.
So that's, that's, that's also way that people use it and having problems configuring.
So that's what I see from issues.
This portion of talk Python to me is brought to you by Bluehost.
Got ideas, but no idea how to build a website?
Get Bluehost.
With their AI design tool, you can quickly generate a high quality, fast loading WordPress site instantly.
Once you've nailed the look, just hit enter and your site goes live.
It's really that simple.
And it doesn't matter whether you're a hobbyist, entrepreneur, or just starting your side hustle.
Bluehost has you covered with built-in marketing and e-commerce tools to help you grow and scale your website for the long haul.
Since you're listening to my show, you probably know Python, but sometimes it's better to focus on what you're creating rather than a custom built website and add another month till you launch your idea.
When you upgrade to Bluehost cloud, you get 100% uptime and 24 seven support to ensure your site stays online through heavy traffic.
Bluehost really makes building your dream website easier than ever.
So what's stopping you?
You've already got the vision.
Make it real.
Visit talkpython.fm/bluehost right now and get started today.
And thank you to Bluehost for supporting the show.
When I saw this feature, I thought, oh, this is fantastic.
I really, really love it.
So let me give people a sense of how this works.
You create an API instance, a little bit like Flask or FastAPI, and use it as a decorator, like at api.get.
And you give it the URL, right?
So what you're saying is I can create an API 1 and API 2, specify a version on it, which will be part of the specification, and then register both of those under different bases.
So like in the example in your docs, you have registering API slash V1 slash an API slash V2 slash.
And then you can have different implementations, but by just doing this little trick, you can have them both in production at the same time.
And I think that's pretty awesome.
Yeah.
And this is like actually like on practice.
I saw that a couple of times here when people like move to some newer version.
They just copy like entire API, I think, change a couple of methods, say, okay, this is version 2, and you're ready to go.
So they don't have to like rely on some kind of external routers to route between different versions.
So they can keep two versions in same code base and don't have any problems with it.
I think one, this sort of UI pattern or this URL pattern, I think you've got to have a little bit of foresight to have API slash V1 slash to start with, right?
But I mean, I guess you could always add slash V2 slash V3 afterwards.
But if people think ahead a little bit and put versioning in there explicitly, that would be nice.
Now, do you have, can you, if a function doesn't change across APIs, can you put, you know, API 1 decorator get and API 2 decorator get onto the same function?
Or do I have to kind of do some redirect, indirection?
That part is probably will not work.
And I think like, so probably someone next releases.
So inside API, you can also define a couple of routers.
Yeah.
So this is kind of, so you can split your application API into like different modules, which called routers.
And yeah, so basically the idea is that you can define, you know, one router with a couple of methods and then use it in version one and version two.
So that's the idea, which is actually, well, it's not ready yet, but this is something that is coming in next release because this is also like very, very requested feature.
So you don't have to even copy your version in code by just reusing stuff that is common and then just overrule the new methods or the new parameters that land it on new version.
Yeah, I really like it.
That's great.
Well, yeah, this is that this is an amazing feature.
When I first saw this, I thought, oh, yeah, this makes version in your API so simple.
And you even get documentation with it just because that's how documentation works, right?
Yep.
And you pointed out that you can have public and private APIs in this similar way.
And so you could have public APIs, maybe multiple versions of them, but also a separate private API that potentially exposes more more things.
Right.
And as you said, you could even put authentication on top of it separately.
Right.
Yep.
I love it.
Let me go back to the homepage.
Let me ask you a question here.
So you said this is another thing that I think I'm a big fan of.
So in your first example, you talked about, oh, if you put parameters into your API and you give them types, they'll be parsed and validated and all that.
But like FastAPI, you can define a Pydantic model that is the type of the thing.
And then the values of that Pydantic model will be validated and populated from different areas.
Right.
So you could have like a customer object or here you've got an item which has a foo and a bar of different types as the parameter to the API.
Right.
This is really key.
So you define it once.
Yeah.
Or maybe you can even define it on a model level.
So you create your table, you know, customer name is string, you know, birth date is like daytime.
Then it can be automatically transferred to the schema, which is Pydantic model under the hood.
And then you call it pass it as an argument.
And yeah, you have your data validated.
It automatically parsed through JSON, extracted, validated.
And you're not only limited to JSON.
So Django Ninja also comes with what is called parsers.
So you can define, okay, is it a YAML parser, XML parser, CSV parser.
So you can define your own parsers.
And then you can just have different inputs.
And yeah, they will parse it.
But yeah, like the core guarantee you have is that your annotated argument that you pass here will always have your log.
If you have name as a string, it will be string.
If your birth date is date, then it will be date.
And you have this kind of guarantee that you can always rely and be sure that it's parsed and validated.
Yeah, that's a big point of this, right?
Once the function gets to where your code actually runs, it's good.
The data has already been validated.
Everything, right?
Yeah, you focus on business logic.
You put aside your mental context about validating stuff.
You're like it's somewhere else.
Someone else defined it.
So you don't worry about it.
You just, in your operation, you focus only on business logic and that's it.
That's super interesting.
And the reason I focus on this is you said that the conversion of input data coming in to the Python model can come from a bunch of different locations.
JSON, path parameters, query parameters, cookies, headers, forms, etc.
Will it take from multiple locations to fill this out?
For example, will it take the body of that JSON and maybe pull the name out of it, but then get the ID from a path parameter or a query parameter?
Will it combine it in this way?
Yeah, basically, like the rules are pretty simple.
So you can actually mark.
So there is like your import from Ninja, like, for example, form or header.
And then in like in this square containers, you can define, okay, this comes from form, let's say.
And that way you can tell, okay, it's just, okay, you parse the data only from form data.
Don't just try to parse it through JSON and stuff like that.
I guess you can search in documentation, try like searching for forms.
That will be a good example.
Form data, yeah.
So you see, you kind of define, so we import form container.
You say, okay, username is a string that should come from form and password is also string here that comes from form.
And that way you can just leverage on default, you know, for multi-pal from data or just default form data.
And yeah, like simply parse it without JSON, but just the raw form data.
So you can say that for individual values, like if you're passing in the username or password.
And you can even do that for your Pydantic models, which is cool.
You say the item, instead of being of type item, you say it's of type form of item, which means go to the form and get it, right?
Yeah.
You must have done some pretty challenging type stuff to get this all to work, right?
To get it.
So how do you specify it comes from the form?
But also that when I say item dot, it gives me lists from items, not from form.
And right.
Is this like an annotated thing or how does this work?
This is actually like, like really kind of hack away.
Like, yeah.
How to make it work.
And then the first, yeah.
Like editor should do that to complete.
My pie should not like scream at you and it should validate under the hood.
So there's like a pretty, pretty like nasty kind of if typing stuff, annotations.
So it's like really like inside it looks ugly, but outside it's like perfect.
Yeah.
So you get like, you just define, okay, it's form from item.
Your editor works, your serializer works, everything works, but under the hood, it's like pretty,
you don't want to maintain, maintain this code.
It's like too much stuff to satisfy all parties, the my pie, the code and the editors.
That's exactly what I was thinking.
It looks simple, but I bet it was, I bet it was tough, but it's really nice.
Yeah.
It's like the differences between 3.7, 3.8, 3.12.
Like they're doing, you know, every like year the new Python works and then there is like
little tweaks here and there.
So it's like, yeah, it's like the, the simplest to keep it simple.
Like it's, yeah, it goes like a lot of testing on, on, on, under the hood.
That testing is probably pretty hard.
I would imagine because you've got different consumers, you know, how do you make sure that
an editor works and that my pie works in the code runs, you know, this is not just maybe
a unit test, a simple unit test.
I imagine.
Well, yeah, I remember when I started like, yeah, like I just, I thought, okay, I'll just
make a 100% coverage on all Python versions.
But yeah, that was like really tricky part because yeah, there was like edge cases for, for some
combinations.
So if you know, maybe like a Jenga plus Python 3.6 or stuff like that was like really giving
some headaches.
But I was at some point lucky that first Sebastian did it for FastAPI.
So I literally just copied lots of test cases from FastAPI codebases, just a little bit
adapted, and I already had a pretty extensive test coverage.
So then I just focused on specific implementations of here and there.
But yeah, the end goal is really the usage should be really simple.
So your editor works.
If you use MyPi, you really like types, you have 100% type correction.
And then on the hood, it does as fast as possible, whatever, like PyDentic Magic,
media with Rust.
It also does it as fast as possible, currently possible with current technologies.
I'm going to go talk through the motivation section because you've got this motivation
page that I think is probably a really good place for people to start as they're considering
this.
There's a lot of interesting pieces here.
For example, some stuff that we haven't touched on like dependency injection and other really,
really wild ways to sort of combine code to make it cleaner and more single purpose functions,
right?
So we talked about the Django Ninja will take inputs from these various locations, validate them, convert them,
then pass them in.
But this example you got here.
You're using external functions to say get a user and then using that as part of the population,
the data, I guess.
Tell us about this.
Basically, this is how FastAPI works.
So they really define all like arguments there.
And if you like, let's say you need a database in your function, like if you're using like SQL
Alchemist, then you're kind of forced to have this DB engine as dependency or like user as a dependency.
And because, you know, with Django, you kind of already have it either like you don't have like to access database,
you just use models dot object and you don't have to worry about.
So this part is for all the way.
And as well, like users also like stuff that by Django protocol, you kind of have it on a request.
Yeah, request that user or request that session.
There's lots of stuff you already have in kind of like settled way and that people already expect it to be there.
So that's why like, yeah, like I was wondering, should Django Ninja have dependencies?
But my vision, it should not.
It should be like a bit more simple and rely on the current way of work where people are already know,
well, okay, where's my user?
Where's my session?
Where's my cookies extra if I need to?
So you worry only about like your arguments are the stuff that should be validated,
not the stuff you depend on.
Yeah, like, you know, database or like some kind of authentication.
And yeah, that's why you can also separate the logic.
So because if you already like entered the execution of the function,
that means all the process that was before is already done.
You have guaranteed that if you define authentication,
that's already done and you have your user.
If you like relied on some kind of validation, then it's already there.
So again, your focus is only on business logic of application and the rest pieces should not be involved.
And don't, you know, you don't bother with them at all.
This is super nice.
So let me try to give people a sense of what I'm talking about here over audio.
And so in the example, like I said, I'll link to the motivation page is you've got this function that needs the database.
It needs the current user and it needs some arguments.
Right.
So the argument is handled by just what comes into the posters, part of the URLs and stuff.
And then there's a database and you can say this depends on a function, which then Django Ninja will call automatically.
And you can say the current user also is a user and it depends on this other function, which actually is an async function and being called for, which is, that's amazing.
This is how FastAPI works.
So it depends is actually not the part of Django Ninja on purpose.
So this is like example how it is in FastAPI, but it is not on Django Ninja because it's like all of the building.
This is the Django, this is the, this is the FastAPI version.
Yeah.
And so you're saying, no, this is what we do instead.
Okay.
Got it.
Yeah.
You know what?
I'm not a big fan of dependency injection.
Not necessarily the arguments are fine, but this, the whole idea of dependency injection, I always find it looks cool in practice.
But then when I'm trying to work with it, I'm like, where does this come from?
Like, why do I have this?
And how do I, how do I get back to it?
You know, I have kind of the same feeling about like any, any like Python approaches to the dependency injection libraries,
because I think the, the Pytonic way is actually like, like it's more kind of, because you can import from strings.
You can, you know, pass kind of any type of arguments and just access them by like the, this quarks.
So you, I think you never should need like, you know, Java, Java world dependency injection, because the, the language itself kind of gives you a way to not call it a dependency, but find kind of verbal work around how to access, you know, one type of class or another.
So you do, so you do, so you don't even call it like in, in, in, in dependency injection while it, you actually like inject it from some configuration.
So like, basically what I try is like, you know, like the, the, I guess the perfect libraries that exist in like Python is like, let's say like requests, right.
They could have done like the same way.
Like, okay, like let's do dependence injection or some kind of SSL protocols, like, you know, timeouts and stuff like that.
But they, they really try to like make it as current Pytonic way as possible through like some quarks or like, or, or like, you know, stuff that you, you never, you never know.
Okay.
What's what's actually injected, but verbally, you know, what, what's, what's going on.
Yeah, the quarks and the stargs, quarks, all that stuff is when I see that it's the function signature, I feel like, okay, the, you kind of given up on helping me know what I'm supposed to do here.
The only way to know is to go read the documentation, which not a big fan.
Let's talk about authentication.
So what's the, the Jingo ninja way for authentication here?
It looks pretty integrated.
It's kind of both integrated and it's pretty like loose and flexible.
So yeah, even, even, so, you know, you just on your ninja API, you define like auth variable.
So it's purposely not, not called authorization or authentication.
It's just auth, you know, so you can define yourself.
Do you want to implement authorization or authentication?
It's like really up to you.
Okay.
Because it's like, yeah, you just, the auth is like a function that will be called with your request.
And then you can kind of define, you can read some kind of cookie or token or like, you know, whatever, like it's like up to your business implementation.
And yeah, if it, if it's, if it's return something, then this is like a, you know, your, your, your object that will be attached to request.
Dot.
Off inside this function.
You can also access request and like attach your request.user, which is what is in most cases are done.
If you like, you have user in your system that is tied to some token or stuff like that, but it's kind of on purpose that yeah.
Like it's, it's not always, you have user.
So maybe you just need some kind of object.
Maybe you have API keys.
Client or yeah, whatever.
Yeah.
Maybe you just need to pass an API key.
It's not tied to someone.
Yeah, for sure.
I tried not to like force people into like deciding what exactly they're implementing.
They just need to implement a function and then use it.
But yeah, by default, you can just import default Django auth that will use like your standard Django session based authentication.
And yeah, just live with that by some default cookies, tokens.
So like whatever you prefer.
What about async?
I think it was like one of the core features.
Yeah.
Like, and what was like different from Django rest framework.
And yeah, it kind of, yeah, it does.
Like I see like from the feedback from companies who use like it is sometimes like truly help them like to, to take a like some kind of slow, slow request that they're like just to waiting for some data.
Yeah.
But yeah, the thing is like you're not, you are not required to use one or another.
So you can pretty much mix both worlds in your, in your code.
So, because, you know, it was Django because initially Django was not designed to be, I think, friendly.
So, and when you, for example, like, because let's say you, you request a user return, but under the hood, let's say it's called a user dot group, which is also will initiate another request to the database under the hood.
So this is, this part is like, it's really hidden from you and you cannot really sometimes be involved in manipulating.
And so that, that request also gets into async context.
So yeah, the simplest way you just can, for some complex stuff, you can just mix two worlds, you know, async and not async functions.
They decide what you need.
You need like some kind of a weight performance while waiting for some, you know, network connection database, or you just need like, you know, to make it simple.
Simple, easy.
And you just don't worry about if it's, you know, that does some kind of a little bit of weight.
And so mom, I would say my, my go to approach is like, yeah, basically I do all the stuff in sync mode.
It's only when I really need to maybe like server side events or like, you know, a weight into some network connection or like long database queries that returns lots of results.
And yeah, this is, this is like, you just switch to async and you just await your result and you win some, some performance out of it.
But by default, I would, I would just go with no, no, no, no, no, no, no.
I think way.
Yeah. I would as well.
Although for my app, at least I found most of the time there was some database call or there was something going on or like, all right, this part is async.
So, you know, work your way back and make the view function async as well.
But if you don't have to, then certainly don't, right?
It just adds complexity that you don't necessarily need.
The reason I'm asking is what's the story with the Django ORM in async these days.
I know that this is kind of one of the last parts for the Django async support journey.
I think they settled on what it is now.
So you can like, there's extra methods on ORM, like a get, a create, a like filter or stuff like that.
So you can already like do a bunch of default stuff.
All right. So those you can await already. And that should be.
It's a tricky part when, you know, you, let's say you have a query set, so you like awaited it, but then inside you, you do, you do some kind of loop and then access some, you know, dot, dot something that is actually foreign key or many to many field that.
So then you need to like, keep in mind that you need to like do, do the join.
Yeah. Like already in your ORM query, like, you know, select related or pre-fetch related that will do for you this, this pre-fetching.
So then on the time when you're accessing some attribute, it will not, it will not perform like a query for you because this is actually like what's, what's actually biting Django ninja users.
So yeah, they, let's say they're creating like schema that outputs user and a group.
Yeah. Like again, a group is some kind of schema.
So they just, okay, I'll just return the async query set and yeah, be, be ready with it.
But then Django kind of, and I think it's probably good that they, they, they tell you like, okay, so you're accessing the attribute that requires the database.
So you kind of get an error.
So they kind of force you to think, okay, you know, if you want to really get, get it correctly, then you just better like evade with, with joint.
So that's good advice for people, even if they're not doing async, right?
Right.
This, this N plus one problem can make your ORMs very slow.
And if you know, I'm going to go access this thing.
That's a foreign key.
Maybe this means like, if you use it by default, you can kind of catch those, those like parts that you were not a word before.
And then you kind of, they will bite you before you go to production.
So maybe that's, that's the way to go.
Some people will like this and some people will not like it.
But yeah, it will definitely let you know what's going on.
Right.
Amazing.
Anything else before?
I guess one thing really quick here is when you say, when you're doing the async stuff, can I just use the Django admin management tool?
Or do I have to run with an async server separately?
What's the story there?
It's just work out of the box.
So yeah, you just run it under you, you record like any other SGI worker and you have your, your, all your, all your default stuff will be there.
So basically it's like, it's out of the box.
It should work.
Like, you know, it's, it's, it's, it's all, you only have tricks only was, you know, this N plus one problem.
So this is where you will have to keep, keep, keep attention and make sure it's like a weighted properly.
Yeah.
UVicorn is really nice.
They're making a lot of progress.
They're now a standalone server.
You don't have to use them.
You don't have to say G unicorn with UVicorn workers.
You can just run it as its own proper Linux server and so on.
It's really nice.
All right.
So that's what brings up.
Is there anything else you want to tell people about Django Ninja that we, we should cover?
I think I would like to talk about the, the, you know, so currently we are in this LLM era where, you know, people no longer, you know, Google or like go, go to documentation.
They just simply ask your, you know, favorite open AI or anthropic chat to like write you some code.
So what, what I currently notice is like, you know, when library do some kind of new iterations, you're like introduce new stuff.
Like, you know, make some kind of, you know, maybe even breaking changes.
You know, the LLMs will not sexually be out time with, with those changes.
And that is actually like bringing lots of problems.
Like, so like the, with issues that I see, like I see that people just, you know, you're working on recently, but probably they, they asked LLM to implement some feature that is already like old syntax and already like, you know, giving in some problems.
So it's really, you know, this problem for like libraries that, you know, want to make changes, but on the other hand, like LLMs will pick it up, you know, maybe in like year best.
Yeah.
Like whatever new version, like I saw this a lot with Pidentic when they, you know, they already like a version two for like a year in a while.
While if you ask LLM to implement some stuff, they, they still giving you the old, old approaches.
So I think this is like, maybe like a, something it's not to open the developer software and source community, the way to influence it.
But yeah, maybe like, so what I, what I am currently thinking is like adding some kind of tooling on top of, or documentation that will just, will give you the, the latest, latest result.
Oh, that'd be cool.
Yeah.
But if you like listening to this podcast, you can go to LLM.jangoNinja.dev.
It will, it will, we'll redirect you to some ticket where I will notify when it's ready.
So like for now, we just redirect to, to GitHub issue.
I will probably need to write better access, but here you can subscribe and I will just try to publish some kind of a better documentation tool in this LLM world where you can ask, you know, a question.
And it will answer you latest, latest and better approaches without like, you know, forgetting all the old knowledge and giving you the, the newer updated version.
So that's, this is a serious annoyance with LLMs because you'll ask it a question on how to do this thing.
And it looks like an amazing answer.
You're like, Oh my gosh, save me so much time.
And then you'll try it and it'll, it'll be using a function or a class or something that doesn't exist.
Like, Oh, you're lying to me, please.
This doesn't exist.
Oh, you're right.
Here's the real way.
And that's, that's even before you get to it being trained exactly on the docs.
So are you using some sort of rag local LLM or what's, what are you doing here?
Generally, like my vision is like, yeah.
So this tooling should give you like some kind of regular latest documentation and also like context with a better, but like recommended by, by the, by the chief ingredients.
Yeah.
So like, no, not like, because I see like, yeah, some, sometimes like you, even on stack overflow, you see some answer, but you technically not, not agree the way it's done, but because it's like on the top and all LLMs already picking it up.
And it's, it's getting like this kind of boost that you don't really want to.
And this is like, I think it becomes kind of problematic that, you know, that you cannot really suggest to large language models.
Okay.
What's the recommended way and why it's better than, than it's currently like suggested.
So yeah, this is, I, yeah, but this is like more like a, it's, it's a new problem that never existed before, but you have to rely on it.
So concurrently, I guess the best way is like to stick with your API forever and just try to, to do stuff on the, under the hood.
So that, you know, users, users always use the, how it was designed at the beginning and yeah, like never, never feel any differences.
Easier said than done.
I will add though, that your documentation has really nice search.
So that that's easy to go here and, and even without the LLM it's pretty approachable.
So it's not, you know, I don't feel like you have to use LLMs to figure this out.
I'm sure people will.
That's actually also the goal, like how, how you make it so that people don't, don't, don't ask any question.
Don't Google like it should be like intuitive.
Let's wrap it up with a thought, I guess, but we talked about using Python typing as a core element of the framework.
And I don't know about other people, but for me, the fact that it has strong typing and maybe put a little bit of that into your code as well.
I find I go to the documentation much less when I do that, or I'm using APIs like that because you just, your editor just tells you, well, here's the three things.
You go, well, this must be the one.
And you know, it guides you down the path.
But if the editor is not there to help, because it doesn't have the type information, then, then you have to go to the docs or you have to go to search and it's just, it's not great.
Yeah.
So I think it's very approachable.
Even if you use some, some stuff like a pilot.
Yeah.
So even if suggested something wrong, but if you like have types that and your editor immediately like highlighted you with some kind of red, red, wavy thing that it's you don't have this attribute and you already like sold it.
And yeah, it's makes you laugh easier.
Yeah, it absolutely does.
All right.
Final thoughts.
People wouldn't get started with Django Ninja.
What do you tell them?
It's definitely worth trying if you like never, because what I often see is like on Reddit on like any, any resources, like people ask, okay, so I have JNDRF and Django Ninja.
So what should I try?
And you're like, there's always like people who answer a bunch of stuff and pros and cons.
Like, yeah, like I think at this point, like, yeah, but before there was like big argument because the Django rest framework is really old stalled product that is like, you know, have like large community.
I think at this point, Django Ninja also becomes this old thing as well, because like, it's already like, I think it's like five, it's five years will be like somewhere this year or more, even more.
I don't count.
It's already like have millions of downloads and it's all like, I know very big, big names of companies who use it and they use it in a pretty kind of sensitive areas.
So yeah, should not, not get worried.
And because also like, I guess one of the things that I'm trying to, as a strategy for this product, I try to be this as, you know, subtle as possible without like, you know, risky moves or changes.
Try to make it for people to migrate to newer, newer versions easier.
So like, I guess the hardest part was like, you know, between version zero something to 1.0, but yeah, it will, it was like dictated by the fact that, you know, Pidentic moved from one to two.
So like, there was no way to make it without breaking changes.
But my goal is like, like actually like, you know, even in this LLM era to try to introduce new features that should not force you to like, you know, may have a breaking changes year in your code.
So yeah, that's my, that's my goal.
And yeah, this is why you should try it.
So if you, if you, if you like try it in introducing to your code, then you probably will be good, good for the, for the good amount of time.
And I think like one more thing that we didn't talk about, but you should definitely try is, is project called nano Django.
So this is a way for you to write a code, like a pro Django project, but just in one file.
So under the hood, it also uses Django Ninja.
So this is also like a very, very easy thing to be, to join the Django Ninja tryout thing.
So here in one file, you can define your model.
You can define your API, run it with one command and you're ready to go.
And basically like same, same way you used to work with Flask or FastAPI.
But the, the coolest thing about this is like when you see, okay, your, your file grow to like, you know, hundreds of thousands of lines.
There is a tooling that you just, it can convert for you, like your single file into a standard Django project structure.
And that, that way you can, you know, you start small, then you see you're growing and then it, it, it will convert you go to, you know, standard Django configuration,
process and all the conventions.
And yeah, you continue your project without, without any,
Yeah.
Nano Django is an interesting project for sure.
It's like, let's start simple, but if you need it to be more structured, then it'll actually explode out the file and stuff.
This is like a very first way to try that to do.
To just simply install Manage Jenga and you'll have Jenga in that hood, Ninja under the hood and everything you need all in one, in one file.
Cool.
Awesome.
I'll definitely put that in the links for people as well.
Well, Vitaly, thank you for being here.
I did mention at the top, but I do want to send out some thoughts and support for the folks out in Ukraine as well.
I know it's a ridiculous time and be safe out there.
You guys.
Thank you.
Thank you for being here.
Thank you for creating this awesome project and coming on the show and sharing it with us.
Thank you for having me.
Thank you very much.
Yeah.
Bye.
This has been another episode of talk Python to me.
Thank you to our sponsors.
Be sure to check out what they're offering.
It really helps support the show.
Take some stress out of your life.
Get notified immediately about errors and performance issues in your web or mobile applications with Sentry.
Just visit talkpython.fm/sentry and get started for free.
And be sure to use the promo code talkpython, all one word.
And this episode is brought to you by Bluehost.
Do you need a website fast?
Get Bluehost.
Their AI builds your WordPress site in minutes and their built-in tools optimize your growth.
Don't wait.
Visit talkpython.fm/bluehost to get started.
Want to level up your Python?
We have one of the largest catalogs of Python video courses over at Talk Python.
Our content ranges from true beginners to deeply advanced topics like memory and async.
And best of all, there's not a subscription in sight.
Check it out for yourself at training.talkpython.fm.
Be sure to subscribe to the show.
Open your favorite podcast app and search for Python.
We should be right at the top.
You can also find the iTunes feed at /itunes, the Google Play feed at /play, and the direct RSS feed at /rss on talkpython.fm.
We're live streaming most of our recordings these days.
If you want to be part of the show and have your comments featured on the air, be sure to subscribe to our YouTube channel at talkpython.fm/youtube.
This is your host, Michael Kennedy.
Thanks so much for listening.
I really appreciate it.
Now get out there and write some Python code.
write some Python code.
Open arms!
you
Thank you.