WEBVTT

00:00:00.001 --> 00:00:05.220
If you're a Django developer, I'm sure you've heard so many people raving about FastAPI and Pydantic.

00:00:05.220 --> 00:00:08.360
But you really love Django, right? And you don't want to switch?

00:00:08.360 --> 00:00:11.820
Well, then you might want to give Django Ninja a serious look.

00:00:11.820 --> 00:00:17.800
Django Ninja is highly inspired by FastAPI, but also deeply integrated into Django itself.

00:00:17.800 --> 00:00:22.600
We have Vitaliy Kucheryaviy, the creator of Django Ninja, on the show to tell us all about it.

00:00:22.600 --> 00:00:27.980
This is Talk Python To Me, episode 490, recorded December 10th, 2024.

00:00:27.980 --> 00:00:30.000
Are you ready for your host?

00:00:30.000 --> 00:00:34.280
You're listening to Michael Kennedy on Talk Python To Me.

00:00:34.280 --> 00:00:38.040
Live from Portland, Oregon, and this segment was made with Python.

00:00:38.040 --> 00:00:44.040
Welcome to Talk Python To Me, a weekly podcast on Python.

00:00:44.040 --> 00:00:46.260
This is your host, Michael Kennedy.

00:00:46.260 --> 00:00:51.620
Follow me on Mastodon, where I'm @mkennedy, and follow the podcast using @talkpython,

00:00:51.620 --> 00:00:54.620
both accounts over at fosstodon.org.

00:00:54.720 --> 00:00:59.520
And keep up with the show and listen to over nine years of episodes at talkpython.fm.

00:00:59.520 --> 00:01:04.020
If you want to be part of our live episodes, you can find the live streams over on YouTube.

00:01:04.020 --> 00:01:09.860
Subscribe to our YouTube channel over at talkpython.fm/youtube and get notified about upcoming

00:01:09.860 --> 00:01:10.340
shows.

00:01:10.340 --> 00:01:12.300
This episode is brought to you by Sentry.

00:01:12.300 --> 00:01:14.080
Don't let those errors go unnoticed.

00:01:14.080 --> 00:01:15.940
Use Sentry like we do here at Talk Python.

00:01:15.940 --> 00:01:19.280
Sign up at talkpython.fm/sentry.

00:01:19.760 --> 00:01:22.040
And this episode is brought to you by Bluehost.

00:01:22.040 --> 00:01:23.660
Do you need a website fast?

00:01:23.660 --> 00:01:24.540
Get Bluehost.

00:01:24.540 --> 00:01:29.900
Their AI builds your WordPress site in minutes, and their built-in tools optimize your growth.

00:01:29.900 --> 00:01:30.860
Don't wait.

00:01:30.860 --> 00:01:34.460
Visit talkpython.fm/bluehost to get started.

00:01:34.460 --> 00:01:35.880
Hey, everyone.

00:01:35.880 --> 00:01:39.340
Before we jump into the interview, I want to tell you about a massive article I wrote I

00:01:39.340 --> 00:01:40.240
think you'll find interesting.

00:01:40.240 --> 00:01:44.860
I did data science things and dug into all the episodes this year and pulled out the top

00:01:44.860 --> 00:01:48.120
five Talk Python To Me episodes of 2024.

00:01:48.120 --> 00:01:53.080
Then I did a deep dive into the content, what was covered, what was important, even learned

00:01:53.080 --> 00:01:55.320
a few things that I missed the first time around.

00:01:55.320 --> 00:02:00.480
Just visit talkpython.fm/blog, and the article is right at the top of the page.

00:02:00.480 --> 00:02:02.720
I'll put the full link in the podcast show notes.

00:02:02.720 --> 00:02:06.420
Also, if you haven't discovered it yet, Talk Python has a blog.

00:02:06.420 --> 00:02:10.660
While you're there, please subscribe to the RSS feed to get all the articles and announcements.

00:02:10.660 --> 00:02:11.680
Thanks for listening.

00:02:11.680 --> 00:02:13.080
Happy holidays, everyone.

00:02:14.080 --> 00:02:15.800
Vitaly, welcome to Talk Python To Me.

00:02:15.800 --> 00:02:16.120
Yep.

00:02:16.120 --> 00:02:16.580
Hello, hello.

00:02:16.580 --> 00:02:17.660
Yeah, thank you for having me.

00:02:17.660 --> 00:02:20.920
This is going to be the first time I've ever had a ninja on the show.

00:02:20.920 --> 00:02:21.880
Okay.

00:02:21.880 --> 00:02:22.420
Okay.

00:02:22.420 --> 00:02:24.060
That will be fun.

00:02:24.060 --> 00:02:24.920
No, Django Ninja.

00:02:24.920 --> 00:02:25.600
Django Ninja.

00:02:25.600 --> 00:02:26.920
It's going to be a lot of fun, isn't it?

00:02:26.920 --> 00:02:27.260
Yep.

00:02:27.260 --> 00:02:27.520
Yep.

00:02:27.520 --> 00:02:27.880
That's right.

00:02:27.880 --> 00:02:28.200
Yeah.

00:02:28.200 --> 00:02:29.480
So welcome to the show.

00:02:29.480 --> 00:02:33.840
We're going to talk about Django Ninja, which is, at least in my mind, you tell me if I'm

00:02:33.840 --> 00:02:36.120
seeing this correctly as we get into it.

00:02:36.120 --> 00:02:40.120
But in my mind, I see Django Ninja as Django's FastAPI.

00:02:40.120 --> 00:02:42.600
That's exactly like the most asked questions I ever had.

00:02:42.700 --> 00:02:44.080
Like, why not just FastAPI?

00:02:44.080 --> 00:02:48.480
So I hope I will answer during this podcast why it is, why it exists.

00:02:48.480 --> 00:02:50.980
And we will all work with this question.

00:02:50.980 --> 00:02:53.360
Ever since it first came out, I was, I saw him.

00:02:53.360 --> 00:02:54.080
That's cool.

00:02:54.080 --> 00:02:55.140
That is a cool idea.

00:02:55.140 --> 00:02:57.680
And I'm pretty excited to talk to you about it.

00:02:57.680 --> 00:02:58.260
It's going to be great.

00:02:58.260 --> 00:03:00.740
Before we get into all that, though, quick introduction.

00:03:00.740 --> 00:03:01.880
Who are you?

00:03:01.880 --> 00:03:02.680
What do you do?

00:03:02.680 --> 00:03:04.400
How do you get into this Python world?

00:03:04.540 --> 00:03:04.940
Yeah.

00:03:04.940 --> 00:03:06.760
So my name is Vitaly Kucherawe.

00:03:06.760 --> 00:03:07.280
Yeah.

00:03:07.280 --> 00:03:07.720
Good luck.

00:03:07.720 --> 00:03:10.160
Good luck repeating that last name for everyone.

00:03:10.160 --> 00:03:12.560
But yeah, that's my name.

00:03:12.560 --> 00:03:13.980
So I'm from Ukraine.

00:03:13.980 --> 00:03:15.260
I'm currently in Ukraine.

00:03:15.660 --> 00:03:19.260
And I'm in tech for like, I don't know, like more than 20 years probably.

00:03:19.260 --> 00:03:24.000
And yeah, I worked with, well, at the beginning of my career, I worked with all sorts of languages,

00:03:24.000 --> 00:03:29.520
technologies, like everything, like C++, .NET, Java, PHP, like everything.

00:03:29.520 --> 00:03:31.620
And yeah, I guess like one time I tried Python.

00:03:31.860 --> 00:03:35.040
And yeah, since then, this is like my go-to tool.

00:03:35.040 --> 00:03:37.600
And yeah, I just cannot switch to anything else.

00:03:37.600 --> 00:03:38.060
Yeah.

00:03:38.060 --> 00:03:40.440
That's how I got there.

00:03:40.440 --> 00:03:50.020
And yeah, like currently I work for a company, Modicos Prime, where we do AI for big pharma companies.

00:03:50.020 --> 00:03:52.460
We help them, you know, innovate in this area.

00:03:52.460 --> 00:03:55.560
And yeah, there I also use Django Ninja.

00:03:55.560 --> 00:03:58.280
And yeah, basically eating my own dog food.

00:03:58.280 --> 00:03:59.560
So that's a good sign.

00:03:59.560 --> 00:04:00.940
That's a very good sign.

00:04:00.940 --> 00:04:07.500
And it's so excellent to be able to work on these projects, but also use them with the team.

00:04:07.500 --> 00:04:14.400
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.

00:04:14.400 --> 00:04:17.700
Basically, I'm watching like how other people use like in a team.

00:04:17.700 --> 00:04:28.260
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.

00:04:28.260 --> 00:04:36.820
So yeah, that kind of helps solve it easier and somewhat more maintainable way and easier to integrate.

00:04:36.820 --> 00:04:38.920
So basically, that's my end goal.

00:04:38.920 --> 00:04:39.680
Yeah, that's awesome.

00:04:39.680 --> 00:04:45.820
There's a really interesting stat from the Python, the PSF and JetBrains survey.

00:04:46.260 --> 00:04:48.220
I'll put it on the screen for people watching.

00:04:48.220 --> 00:04:58.720
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?

00:04:58.720 --> 00:05:01.700
They're kind of, if you look at the survey results, they're kind of similar.

00:05:02.020 --> 00:05:10.780
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.

00:05:10.780 --> 00:05:12.740
50% more or something like that.

00:05:12.740 --> 00:05:13.220
Not quite.

00:05:13.220 --> 00:05:24.460
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?

00:05:24.460 --> 00:05:25.900
This is pretty much described it.

00:05:25.900 --> 00:05:31.920
I think like, yeah, before this survey, like there was like Flask and Django, like, you know, there was like a tie.

00:05:32.280 --> 00:05:38.180
But now, like basically, I think FastAPI is eating a lot of this pie from Flask and Django.

00:05:38.180 --> 00:05:43.120
And yeah, you see now it kind of gets into like, you know, three main players.

00:05:43.120 --> 00:05:51.580
And yeah, like from what I experienced is I think it's actually like maybe developers are actually splitting into like two main groups.

00:05:51.580 --> 00:05:55.880
Like ones who like to use some kind of tool that have everything like Django.

00:05:55.880 --> 00:05:58.080
Yeah, like it can be in the Java world.

00:05:58.080 --> 00:06:00.640
It can be like Spring framework in Python and Django.

00:06:01.020 --> 00:06:06.400
While others like to, you know, start from scratch and like combine all little pieces together.

00:06:06.400 --> 00:06:13.760
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.

00:06:13.760 --> 00:06:17.160
And this is where the market for Flask and FastAPI.

00:06:17.160 --> 00:06:21.280
While for other folks, it's like more like, yeah, they just want something ready.

00:06:21.280 --> 00:06:24.160
Like whatever issues it have, let's take it.

00:06:24.160 --> 00:06:26.200
It's just, you know, more standardized way.

00:06:26.580 --> 00:06:36.900
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.

00:06:36.900 --> 00:06:42.740
So like my vision of Django is like it should be a little bit glued to two of these worlds.

00:06:42.960 --> 00:06:51.640
Because what I noticed is like when people use Django Ninja, it's mainly like all the applications is actually like it's API.

00:06:51.640 --> 00:06:52.780
There is no form.

00:06:52.780 --> 00:06:54.420
There is no Django admin.

00:06:54.420 --> 00:06:56.940
There is no like, you know, custom views.

00:06:56.940 --> 00:07:00.480
You're like, so they basically tie to API and doing just the rest API.

00:07:00.980 --> 00:07:08.200
And I think, yeah, this is basically when it shines and when you can take like an approach like for FastAPI Flask.

00:07:08.200 --> 00:07:11.680
But under the hood, it will have all the batteries that comes with Django.

00:07:11.680 --> 00:07:17.520
So I think that's how it's, why it's actually like picking up and why it works and why people use it.

00:07:17.520 --> 00:07:22.820
I know there are a lot of people out there that have just some kind of JavaScript front end and a bunch of APIs.

00:07:22.820 --> 00:07:29.240
But there's also a lot of websites that are more server side based, more holistic, kind of like Django does.

00:07:29.240 --> 00:07:37.840
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.

00:07:37.840 --> 00:07:39.360
That's a really cool aspect of it.

00:07:39.360 --> 00:07:44.320
It's more interesting like in history, like why actually Django Ninja appear in Havio.

00:07:44.320 --> 00:07:45.240
Yeah, yeah.

00:07:45.240 --> 00:07:45.980
Back in the day.

00:07:45.980 --> 00:07:50.880
So yeah, when Sebastian released FastAPI, I was like really like mind blown.

00:07:50.880 --> 00:07:53.680
Like, this is like, this is all I wanted.

00:07:53.680 --> 00:08:04.700
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.

00:08:04.700 --> 00:08:10.880
So yeah, like for lazy people like me, that was like, you know, like, yes, literally my mind blowing.

00:08:10.880 --> 00:08:11.340
Yeah.

00:08:11.340 --> 00:08:15.420
So basically I tried it out, kind of worked for me for like, you know, one project.

00:08:15.420 --> 00:08:19.700
And that year, like for me, the case was like, I had to integrate it with Django.

00:08:19.700 --> 00:08:22.920
So I had existing application with some database and stuff.

00:08:22.920 --> 00:08:26.140
And I just needed like simple little API to add there.

00:08:26.140 --> 00:08:30.200
So I decided, okay, I'll just take FastAPI for that.

00:08:30.200 --> 00:08:36.020
So I took it, it worked, but the problem is like to FastAPI and the hood uses Starlet.

00:08:36.020 --> 00:08:43.900
And there's like a little bit different approaches to like, like local storage and threads when you're basically accessing the database.

00:08:44.040 --> 00:08:51.720
It's not like without like special hooks and crooks to like access like database within Starlette context.

00:08:51.720 --> 00:08:53.940
And you can kind of lose this connection.

00:08:53.940 --> 00:08:57.940
And then you get just, you know, error that you cannot really debug or do something with it.

00:08:57.940 --> 00:09:01.640
So it was like, like for me, it was like, oh, come on.

00:09:01.640 --> 00:09:05.660
Like it was so, so perfect, but it just breaks in the middle of something.

00:09:05.860 --> 00:09:08.200
And yeah, but I tried like Django community.

00:09:08.200 --> 00:09:10.300
They said, yeah, it's not, it's not, it's not something wrong.

00:09:10.300 --> 00:09:11.400
FastAPI community.

00:09:11.400 --> 00:09:14.340
They said, yeah, it's, it's not something that we do as well.

00:09:14.340 --> 00:09:15.240
It's a Django problem.

00:09:15.240 --> 00:09:16.380
It's a Django problem.

00:09:16.380 --> 00:09:18.600
It's a fastAPI problem.

00:09:18.600 --> 00:09:19.480
Yeah.

00:09:19.480 --> 00:09:26.840
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.

00:09:26.840 --> 00:09:27.040
Yeah.

00:09:27.040 --> 00:09:34.040
You just take input from HTTP request, you apply by the intake models validation and yeah, and that's it.

00:09:34.040 --> 00:09:34.660
And it works.

00:09:34.660 --> 00:09:39.200
So like for me, the first prototype was like done in, in a weekend and it was already like working.

00:09:39.200 --> 00:09:50.500
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.

00:09:51.020 --> 00:09:59.680
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.

00:09:59.680 --> 00:10:09.080
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.

00:10:09.080 --> 00:10:11.540
And yeah, that's, that's how, how it born.

00:10:11.540 --> 00:10:20.900
And I guess the next part is like actually why it exists is like, yeah, so in Pydantic, there is model word is reserved for like, you know, any kind of validation.

00:10:20.900 --> 00:10:21.580
Yeah.

00:10:21.580 --> 00:10:29.160
So Pydantic uses word model for validation, but in Django world model means like tables, yeah, database tables.

00:10:29.160 --> 00:10:31.440
So you cannot just bring Pydantic models.

00:10:31.440 --> 00:10:31.880
It's the ORM, yeah.

00:10:31.880 --> 00:10:32.500
Yeah.

00:10:32.500 --> 00:10:39.580
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?

00:10:39.720 --> 00:10:46.840
So like for me, the main goal was to introduce class called schema, which is like a Pydantic model under the hood.

00:10:46.840 --> 00:10:50.860
And then it should like hide as much as possible the word model.

00:10:50.860 --> 00:11:01.300
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.

00:11:01.300 --> 00:11:09.460
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.

00:11:09.600 --> 00:11:12.560
Like open, open API standard names it.

00:11:12.560 --> 00:11:14.460
So it kind of fits there perfectly.

00:11:14.460 --> 00:11:17.560
And, and yeah, that's kind of how it works.

00:11:17.560 --> 00:11:21.860
And yeah, then, then I was sitting like, it was, what should be the name of the thing?

00:11:21.860 --> 00:11:24.880
Like it was also the hardest part.

00:11:24.880 --> 00:11:32.140
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.

00:11:32.140 --> 00:11:34.280
It was like really lightweight.

00:11:34.280 --> 00:11:36.860
So way faster than Django REST framework.

00:11:36.860 --> 00:11:38.880
So I thought, okay, she should be something fast.

00:11:38.880 --> 00:11:40.960
Try it, you know, word here and there.

00:11:40.960 --> 00:11:41.180
Okay.

00:11:41.180 --> 00:11:42.640
And then, and it become ninja.

00:11:42.640 --> 00:11:45.320
So that's how it's important.

00:11:45.480 --> 00:11:46.280
The naming is perfect.

00:11:46.280 --> 00:11:47.420
The naming is just perfect.

00:11:47.420 --> 00:11:48.840
You and me both.

00:11:48.940 --> 00:11:54.840
When I first saw FastAPI as well, I'm like, this is the way this is how it should be done.

00:11:54.980 --> 00:12:00.440
And FastAPI is really interesting when it came on the scene and by way of being very similar.

00:12:00.440 --> 00:12:16.020
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.

00:12:16.020 --> 00:12:18.840
So, and I saw that like, yeah, this is the way you should build APIs.

00:12:18.840 --> 00:12:23.080
And so when I saw Django Ninja, I'm like, of course Django should have this way.

00:12:23.080 --> 00:12:23.760
This is the way.

00:12:23.760 --> 00:12:25.040
The whole concept is perfect.

00:12:25.040 --> 00:12:25.320
Yeah.

00:12:25.320 --> 00:12:29.480
We just, you make a type hint and then it does validation for you.

00:12:29.480 --> 00:12:34.040
The linting, it does the, you know, editor auto-completion, like everything in one package.

00:12:34.040 --> 00:12:39.180
And yeah, like for, if you're lazy, you just thought I did one type in.

00:12:39.180 --> 00:12:42.960
Like before that, I was like pretty skeptical, skeptical about typing.

00:12:42.960 --> 00:12:45.740
Like, yeah, you just blather your code.

00:12:45.740 --> 00:12:52.540
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.

00:12:52.540 --> 00:13:02.260
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.

00:13:02.260 --> 00:13:10.720
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.

00:13:10.720 --> 00:13:14.900
So you have like, it's really hard to read when you fully type your code.

00:13:14.900 --> 00:13:22.240
So even in my day, day to day projects, like what I basically try to do, I only do type annotation for arguments.

00:13:22.240 --> 00:13:25.900
The rest is actually like editors do perfectly.

00:13:25.900 --> 00:13:30.320
So if you return some object, the editor know, okay, this is like this type or that type.

00:13:30.320 --> 00:13:34.200
And you don't have to like annotate what exactly does it return.

00:13:34.200 --> 00:13:35.360
So a hundred percent.

00:13:35.360 --> 00:13:37.260
That's exactly the way that I do it as well.

00:13:37.260 --> 00:13:45.120
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.

00:13:45.120 --> 00:13:52.160
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.

00:13:52.460 --> 00:13:58.160
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.

00:13:58.160 --> 00:13:58.380
Yum.

00:13:58.380 --> 00:14:03.080
This portion of Talk Python To Me is brought to you by Sentry.

00:14:03.080 --> 00:14:04.180
Code breaks.

00:14:04.180 --> 00:14:05.480
It's a fact of life.

00:14:05.480 --> 00:14:07.620
With Sentry, you can fix it faster.

00:14:07.620 --> 00:14:13.260
As I've told you all before, we use Sentry on many of our apps and APIs here at Talk Python.

00:14:13.260 --> 00:14:19.060
I recently used Sentry to help me track down one of the weirdest bugs I've run into in a long time.

00:14:19.060 --> 00:14:20.000
Here's what happened.

00:14:20.220 --> 00:14:31.760
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.

00:14:31.760 --> 00:14:37.760
The bizarre part was that our logging of that unusual condition itself was crashing.

00:14:37.760 --> 00:14:40.960
How is it possible for our log to crash?

00:14:40.960 --> 00:14:43.560
It's basically a glorified print statement.

00:14:43.560 --> 00:14:45.240
Well, Sentry to the rescue.

00:14:45.680 --> 00:14:51.720
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.

00:14:51.720 --> 00:14:54.740
And because it's production, debuggers are out of the question.

00:14:54.740 --> 00:15:06.640
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.

00:15:06.820 --> 00:15:08.680
That's the person who actually experienced the crash.

00:15:08.680 --> 00:15:11.520
Dictionaries of data on the call stack and so much more.

00:15:11.520 --> 00:15:12.460
What was the problem?

00:15:12.460 --> 00:15:22.160
I initialized the logger with the string info for the level rather than the enumeration dot info, which was an integer based enum.

00:15:22.520 --> 00:15:28.720
So the logging statement would crash, saying that I could not use less than or equal to between strings and ints.

00:15:28.720 --> 00:15:30.140
Crazy town.

00:15:30.140 --> 00:15:36.320
But with Sentry, I captured it, fixed it, and I even helped the user who experienced that crash.

00:15:36.320 --> 00:15:37.760
Don't fly blind.

00:15:37.760 --> 00:15:39.440
Fix code faster with Sentry.

00:15:39.440 --> 00:15:43.460
Create your Sentry account now at talkpython.fm/sentry.

00:15:43.580 --> 00:15:55.840
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.

00:15:55.840 --> 00:15:58.820
All right, well, let's dive into Django Ninja.

00:15:58.820 --> 00:16:04.100
I guess people got a pretty good sense that it's like FastAPI for Django.

00:16:04.100 --> 00:16:06.060
If somebody asks you, what is Django Ninja?

00:16:06.060 --> 00:16:06.980
How do you describe it?

00:16:06.980 --> 00:16:07.820
What do you tell them?

00:16:07.820 --> 00:16:10.500
So basically like extra battery for making.

00:16:10.500 --> 00:16:12.200
So Django is all about batteries.

00:16:12.860 --> 00:16:18.700
So it's that, I don't know, like it's more like, not a battery, but more like, you know, the energy pack.

00:16:18.700 --> 00:16:24.200
If you need API and it's, you need to be, it's like pretty standard way.

00:16:24.200 --> 00:16:31.420
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.

00:16:31.420 --> 00:16:36.880
But yeah, like I think they kind of now become the defector and everything supports it.

00:16:36.880 --> 00:16:40.500
Like, yeah, like the big players, like open AI supports open API.

00:16:40.760 --> 00:16:44.780
So that kind of makes it easier to integrate with all sorts of tools.

00:16:44.780 --> 00:16:47.540
And yeah, there's all different UIs.

00:16:47.540 --> 00:16:47.780
Yeah.

00:16:47.780 --> 00:16:49.460
Like how you can present your API.

00:16:49.460 --> 00:16:51.820
So that kind of a big win.

00:16:52.400 --> 00:17:01.680
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.

00:17:01.680 --> 00:17:01.820
Yeah.

00:17:01.820 --> 00:17:03.740
Like the list of some objects from your database.

00:17:03.740 --> 00:17:04.060
Yeah.

00:17:04.060 --> 00:17:09.700
You simply just return query set and then it's validated and described correctly in your documentation.

00:17:10.180 --> 00:17:20.100
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.

00:17:20.100 --> 00:17:20.400
Yeah.

00:17:20.400 --> 00:17:26.400
Like, so you, you just, you, you just describe your, okay, this is a list of, you know, hero objects.

00:17:26.400 --> 00:17:28.900
And then you return like queries and that's it.

00:17:28.900 --> 00:17:35.460
So you don't have to very worry about like converting the data, serialization, deserialization.

00:17:35.460 --> 00:17:44.400
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.

00:17:44.400 --> 00:17:51.980
And this is, you know, 90% of business logic is usually like, okay, give me the JSON and return to JSON.

00:17:51.980 --> 00:17:55.220
And then you just need some kind of easy glue in between.

00:17:55.220 --> 00:17:59.720
I got to make one or two database queries and then here are the database objects, kind of.

00:17:59.720 --> 00:18:10.860
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?

00:18:10.860 --> 00:18:15.280
When Sebastian built FastAPI, he could just go, well, whatever Pydantic does, I'm just going to do that.

00:18:15.280 --> 00:18:16.400
And here we go.

00:18:16.400 --> 00:18:18.620
Not to downplay the effort he's put into it.

00:18:18.620 --> 00:18:27.040
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?

00:18:27.040 --> 00:18:29.540
So this integration that you put between those, so they're pretty seamless.

00:18:29.540 --> 00:18:30.360
That's really cool.

00:18:30.360 --> 00:18:30.600
Yeah.

00:18:30.600 --> 00:18:36.160
This is basically like whenever I tried it, then it is something that you definitely need to like.

00:18:36.160 --> 00:18:41.980
And it's not only like, because Django comes with like way, way, way more stuff.

00:18:41.980 --> 00:18:47.320
And yeah, like because what Django Ninja comes included is like the caching.

00:18:47.320 --> 00:18:47.760
Yeah.

00:18:47.760 --> 00:18:56.100
That basically fully relies on Django, like a little bit of throttling, filtering, and you know, the fields integration.

00:18:56.100 --> 00:19:02.800
Like, so you define some kind of columns on your database and that's automatically converted to your schema types.

00:19:02.920 --> 00:19:07.820
It's like, you know, so you don't have to like repeat your work two times.

00:19:07.820 --> 00:19:11.160
You're like define, you know, first database tables and then schema tables.

00:19:11.160 --> 00:19:15.680
So I think this is like, again, kind of lazy approach, but it works and it's perfect.

00:19:15.680 --> 00:19:16.980
This is really, really awesome.

00:19:16.980 --> 00:19:24.060
I don't want to make this, I don't want to put you guys, your project in sort of competing against another project.

00:19:24.060 --> 00:19:30.140
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?

00:19:30.140 --> 00:19:35.260
It's more like, you know, a stealth player that, you know, takes majority.

00:19:35.260 --> 00:19:41.840
Like, I think Django Ninja is still like, you know, was it like 5% or something like that on the latest polls?

00:19:41.840 --> 00:19:52.460
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.

00:19:52.460 --> 00:20:03.840
But yeah, again, like the, the, basically, I think the problem that is solved with like this Pydantic validation and serialization is like, yeah, you simply rely on like latest standards.

00:20:04.380 --> 00:20:12.880
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.

00:20:12.880 --> 00:20:13.100
Yeah.

00:20:13.100 --> 00:20:21.860
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.

00:20:21.860 --> 00:20:34.040
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.

00:20:34.040 --> 00:20:43.620
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.

00:20:43.620 --> 00:20:49.780
And then you have like runtime guarantee that whatever type you wrote there, you can just rely it.

00:20:49.780 --> 00:20:55.760
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.

00:20:55.920 --> 00:21:02.460
So you, you, you have this guarantee and then you, it kind of brings away from you this validation hassle.

00:21:02.460 --> 00:21:15.080
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.

00:21:15.080 --> 00:21:21.580
As you were describing that, I was thinking, think of all the value that Pydantic adds to working with JSON data.

00:21:21.580 --> 00:21:23.820
You get that for your API at the boundary, right?

00:21:23.820 --> 00:21:24.360
With no effort.

00:21:24.640 --> 00:21:25.180
That's pretty amazing.

00:21:25.180 --> 00:21:25.380
Yeah.

00:21:25.380 --> 00:21:26.880
Pydantic is just, it's so good.

00:21:26.880 --> 00:21:28.320
What about performance?

00:21:28.320 --> 00:21:32.640
I know you have a, a graph, which I love it.

00:21:32.640 --> 00:21:37.880
I never actually like remeasure it again, but that's how it worked at the beginning.

00:21:37.880 --> 00:21:49.460
So 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.

00:21:49.460 --> 00:21:53.700
So you can, you can, you can, you could immediately have a boost on your performance.

00:21:53.700 --> 00:21:55.400
If you just, you know, free from database.

00:21:55.400 --> 00:21:58.660
So you can just return your, your query set as a sync.

00:21:58.660 --> 00:22:03.680
And then you rely on, you know, event loop to pull those data and return it back to the clients.

00:22:03.680 --> 00:22:13.600
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.

00:22:13.600 --> 00:22:20.360
Or like their CPU usage was like a, you know, load average about like 50% drop to like 5%.

00:22:20.360 --> 00:22:20.620
Yeah.

00:22:20.620 --> 00:22:26.060
So that was like really a immediate, immediate boost up, but it only works for you.

00:22:26.060 --> 00:22:32.820
If you're like, yeah, for, for like very simple case when you just need to parse data, insert database, return, return the result.

00:22:32.820 --> 00:22:39.640
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.

00:22:39.640 --> 00:22:39.960
Yeah.

00:22:39.960 --> 00:22:41.000
And people want to see the graph.

00:22:41.000 --> 00:22:45.000
They can just visit django-ninja.dev and it's right there on the homepage.

00:22:45.000 --> 00:22:52.100
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.

00:22:52.100 --> 00:22:56.520
You will have to, again, rely on number of processes or threads or stuff like that.

00:22:56.520 --> 00:22:59.380
Did you do this analysis with Pydantic 1 or Pydantic 2?

00:22:59.480 --> 00:23:01.440
This is Pydantic 1.

00:23:01.440 --> 00:23:01.840
Yeah.

00:23:01.840 --> 00:23:04.760
So I never actually measured like what is the performance.

00:23:04.760 --> 00:23:14.880
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.

00:23:14.880 --> 00:23:23.520
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.

00:23:23.520 --> 00:23:25.640
But yeah, it was just small, small inputs.

00:23:25.640 --> 00:23:25.940
Yeah.

00:23:25.940 --> 00:23:27.580
It will not be that dramatic.

00:23:27.580 --> 00:23:28.120
I see.

00:23:28.260 --> 00:23:33.180
So if it's just a small input, then Pydantic is not the main part of what you're doing.

00:23:33.180 --> 00:23:36.580
But if it becomes large, then the parsing becomes a big part of it.

00:23:36.580 --> 00:23:36.740
Yeah.

00:23:36.740 --> 00:23:36.920
Yeah.

00:23:36.920 --> 00:23:39.680
There is also like one of the features of Django Ninja.

00:23:39.680 --> 00:23:43.700
So you can replace your, your parsers and serializers.

00:23:43.700 --> 00:23:45.400
So you can say, okay, you know what?

00:23:45.400 --> 00:23:47.600
Let's use or Json for inputting out.

00:23:47.600 --> 00:23:52.860
And again, you can, you can get some extra performance just because you have those parsers are quicker.

00:23:52.860 --> 00:23:54.540
They have their own quirks.

00:23:54.540 --> 00:23:58.960
Maybe not some, something like not standard, but for the, for the most cases, it just, you

00:23:58.960 --> 00:24:03.580
can increase your performance with just, you know, adding a few configuration lines.

00:24:03.580 --> 00:24:03.980
Yeah.

00:24:03.980 --> 00:24:04.060
Yeah.

00:24:04.060 --> 00:24:04.300
Yeah.

00:24:04.300 --> 00:24:05.100
That's really interesting.

00:24:05.100 --> 00:24:10.820
If you know that whatever difference those other faster parsers might have, it doesn't apply to the data you're exchanging.

00:24:10.820 --> 00:24:11.740
Just use them.

00:24:11.740 --> 00:24:11.920
Right.

00:24:11.920 --> 00:24:12.120
Yeah.

00:24:12.120 --> 00:24:14.840
What about uv loop for the async side of things?

00:24:14.840 --> 00:24:17.720
The thing is like, it's really like Django Ninja has nothing to do with it.

00:24:17.720 --> 00:24:21.060
It's fully relies on, on Django itself under the hood.

00:24:21.060 --> 00:24:33.020
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.

00:24:33.020 --> 00:24:34.540
And then you, you work on it.

00:24:34.540 --> 00:24:43.020
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.

00:24:43.020 --> 00:24:43.240
Right.

00:24:43.240 --> 00:24:47.320
Like that thing you just specify that runs on uv loop and then off it goes.

00:24:47.320 --> 00:24:47.580
Yeah.

00:24:47.580 --> 00:24:47.720
Yeah.

00:24:47.720 --> 00:24:48.040
Yeah.

00:24:48.040 --> 00:24:56.400
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.

00:24:56.400 --> 00:24:57.200
That's, that's fun.

00:24:57.200 --> 00:25:01.960
So let's see, let's talk through, give us a sense of what programming this looks like.

00:25:01.960 --> 00:25:11.000
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.

00:25:11.000 --> 00:25:16.760
You import Ninja API from Ninja, and this is your like main kind of API instance.

00:25:16.760 --> 00:25:28.700
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.

00:25:28.800 --> 00:25:34.800
You just decorate some functions and then inside your functions, you are, you know, you do your business logic.

00:25:34.800 --> 00:25:42.740
But the cool part is that you can define input parameters, which are basically gathered from your request.

00:25:42.740 --> 00:25:48.740
So if you do define a couple variables type in, they will, by default, they will come from like query parameters.

00:25:48.740 --> 00:25:54.840
If you define like more complex object, it will come from like your body and it will be parsed from, from JSON.

00:25:55.340 --> 00:25:59.920
And yeah, like you define those functions, you include it into your URL patterns.

00:25:59.920 --> 00:26:00.740
That's, that's it.

00:26:00.740 --> 00:26:02.520
You have your, your ready API.

00:26:02.520 --> 00:26:07.280
You don't have to like do like, put it into installed apps or like configure any settings.

00:26:07.280 --> 00:26:11.820
So yeah, like my goal is also to create as simple as possible, like integration.

00:26:11.820 --> 00:26:15.660
So just in two, three lines, you're ready, you have already API.

00:26:16.120 --> 00:26:20.180
And yeah, that's, that's basically like did the whole, the whole, the whole thing.

00:26:20.180 --> 00:26:21.460
That's it, right?

00:26:21.460 --> 00:26:22.580
There's not a whole lot to it.

00:26:22.580 --> 00:26:27.560
There's also like for, I guess for, for the difference for folks who work with FastAPI.

00:26:27.840 --> 00:26:35.020
The key difference is that Django Ninja, Django Ninja's operation, their function, the first argument is always request.

00:26:35.020 --> 00:26:39.020
Because this is like historically what Jenga, what Jenga protocol is.

00:26:39.020 --> 00:26:39.220
Yeah.

00:26:39.220 --> 00:26:41.820
Like your, your first argument to your view function is request.

00:26:41.820 --> 00:26:51.380
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.

00:26:51.560 --> 00:26:51.680
Okay.

00:26:51.680 --> 00:26:53.220
So this is like kind of important.

00:26:53.220 --> 00:26:58.840
And this is like also like the key, the key difference or requests is request is always there as a first argument.

00:26:58.840 --> 00:27:02.400
And yeah, like there was a suggestion, so let's remove it.

00:27:02.400 --> 00:27:07.080
But I think, yeah, we should keep it, we should keep it consistent with, with, with default Jenga view.

00:27:07.080 --> 00:27:09.240
And yeah, like, I think that, that, that, that works perfectly.

00:27:09.240 --> 00:27:10.000
I don't remove it.

00:27:10.000 --> 00:27:10.460
I like it.

00:27:10.460 --> 00:27:17.720
I think, I know FastAPI is optional to have the request or not have the request, which, which is not a bad choice.

00:27:17.720 --> 00:27:30.580
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.

00:27:30.580 --> 00:27:36.100
And in that world, you know, you just have this thread local request object.

00:27:36.100 --> 00:27:38.520
And that always just seems like breaking the rules to me.

00:27:38.520 --> 00:27:42.500
It's like, you know, what if I want to run this, another thread, how do I capture it and send it?

00:27:42.500 --> 00:27:45.600
I just like having the object just passed in is nice.

00:27:45.600 --> 00:27:46.700
I think I like it.

00:27:46.700 --> 00:27:47.780
I think it's a clean design.

00:27:47.780 --> 00:27:50.660
Yeah, it's like clear or like more open.

00:27:50.660 --> 00:27:50.900
Yeah.

00:27:50.900 --> 00:27:54.120
So you can always know, okay, this is something about, about the HTTP request.

00:27:54.120 --> 00:27:54.860
So it's always there.

00:27:54.860 --> 00:27:56.860
A little easier for testing and reasoning about it.

00:27:56.860 --> 00:28:03.680
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?

00:28:03.680 --> 00:28:06.660
Or, you know, like there's a lot of ambiguity in here.

00:28:06.660 --> 00:28:08.700
It's just like, yeah, here's the request object and go with it.

00:28:08.700 --> 00:28:10.760
So that I'm, I'm, I'm a fan.

00:28:10.760 --> 00:28:11.660
I'm a fan of it.

00:28:11.660 --> 00:28:23.200
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?

00:28:23.200 --> 00:28:27.620
You just go to slash API slash docs, and you get this nice UI, right?

00:28:27.720 --> 00:28:30.540
You don't have any hassle of creating some UIs.

00:28:30.540 --> 00:28:32.360
You just, you just have it for free, right?

00:28:32.360 --> 00:28:35.720
So you just go to slash API slash docs, and there you have it.

00:28:35.720 --> 00:28:42.220
And you can already like, so by default, Django Ninja comes with two, like three doc and a swagger.

00:28:42.220 --> 00:28:50.840
But there is technically like pretty easy to integrate any other tool that can work with, with open API specification.

00:28:50.840 --> 00:29:02.080
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.

00:29:02.080 --> 00:29:04.260
This is a very Django like feature as well.

00:29:04.260 --> 00:29:14.600
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.

00:29:14.600 --> 00:29:21.540
And this is sort of, Hey, you just get this documentation and this testing tool for exploring your API just by creating it.

00:29:21.540 --> 00:29:27.200
Basically it was Django who first sort of, yeah, we just give you admin for free, just define a couple of classes in there.

00:29:27.200 --> 00:29:34.840
And I think like, I think the Django REST framework was also like the first who like gave you the UI also for free.

00:29:34.840 --> 00:29:43.040
So you just define your serializers, you create your classes stuff, and you have UI that you can test.

00:29:43.040 --> 00:29:51.720
So I think like the UI testing is like one of the most kind of best debugging tool you can, you can have.

00:29:51.720 --> 00:29:51.920
Yeah.

00:29:51.920 --> 00:29:56.040
So you like basically like I use it like day to day.

00:29:56.040 --> 00:29:57.220
They are like define your function.

00:29:57.220 --> 00:30:00.080
I just try it out on three UI and that's it.

00:30:00.080 --> 00:30:02.040
That's like very, very, very helpful.

00:30:02.040 --> 00:30:02.840
You can turn it off.

00:30:02.840 --> 00:30:03.080
Right?

00:30:03.080 --> 00:30:03.500
Yeah, sure.

00:30:03.500 --> 00:30:07.160
It's really nice for, if you're making a public API, it's nice to just have it.

00:30:07.160 --> 00:30:17.660
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?

00:30:17.660 --> 00:30:21.620
There is an option to define your custom authentication for the docs.

00:30:21.620 --> 00:30:24.500
You can define, okay, docs, doc outs or something like that.

00:30:24.500 --> 00:30:29.360
You can say, okay, only like, you know, Django super users can access this documentation.

00:30:29.860 --> 00:30:33.860
So it's also like a pretty common request or feature that people use.

00:30:33.860 --> 00:30:35.620
And as well, you can, yeah.

00:30:35.620 --> 00:30:40.020
So I guess one of the interesting part that, that also differs from FastAPI.

00:30:40.020 --> 00:30:45.340
So you can create like, you know, as, as, as many as possible ninja API instances.

00:30:45.340 --> 00:30:57.260
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.

00:30:57.260 --> 00:31:05.660
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.

00:31:05.660 --> 00:31:11.780
So that's, that's, that's also way that people use it and having problems configuring.

00:31:11.780 --> 00:31:13.360
So that's what I see from issues.

00:31:15.540 --> 00:31:18.860
This portion of talk Python to me is brought to you by Bluehost.

00:31:18.860 --> 00:31:22.120
Got ideas, but no idea how to build a website?

00:31:22.120 --> 00:31:23.220
Get Bluehost.

00:31:23.220 --> 00:31:29.720
With their AI design tool, you can quickly generate a high quality, fast loading WordPress site instantly.

00:31:29.720 --> 00:31:33.400
Once you've nailed the look, just hit enter and your site goes live.

00:31:33.400 --> 00:31:34.420
It's really that simple.

00:31:34.420 --> 00:31:38.980
And it doesn't matter whether you're a hobbyist, entrepreneur, or just starting your side hustle.

00:31:39.360 --> 00:31:46.080
Bluehost has you covered with built-in marketing and e-commerce tools to help you grow and scale your website for the long haul.

00:31:46.080 --> 00:31:56.000
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.

00:31:56.000 --> 00:32:04.580
When you upgrade to Bluehost cloud, you get 100% uptime and 24 seven support to ensure your site stays online through heavy traffic.

00:32:05.280 --> 00:32:08.420
Bluehost really makes building your dream website easier than ever.

00:32:08.420 --> 00:32:09.680
So what's stopping you?

00:32:09.680 --> 00:32:10.980
You've already got the vision.

00:32:10.980 --> 00:32:11.760
Make it real.

00:32:11.760 --> 00:32:16.700
Visit talkpython.fm/bluehost right now and get started today.

00:32:16.700 --> 00:32:19.200
And thank you to Bluehost for supporting the show.

00:32:19.200 --> 00:32:24.060
When I saw this feature, I thought, oh, this is fantastic.

00:32:24.060 --> 00:32:25.640
I really, really love it.

00:32:25.640 --> 00:32:27.660
So let me give people a sense of how this works.

00:32:27.660 --> 00:32:34.440
You create an API instance, a little bit like Flask or FastAPI, and use it as a decorator, like at api.get.

00:32:34.440 --> 00:32:35.700
And you give it the URL, right?

00:32:35.700 --> 00:32:47.140
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.

00:32:47.140 --> 00:32:54.480
So like in the example in your docs, you have registering API slash V1 slash an API slash V2 slash.

00:32:54.680 --> 00:33:02.200
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.

00:33:02.200 --> 00:33:03.220
And I think that's pretty awesome.

00:33:03.220 --> 00:33:03.600
Yeah.

00:33:03.600 --> 00:33:05.560
And this is like actually like on practice.

00:33:05.560 --> 00:33:08.980
I saw that a couple of times here when people like move to some newer version.

00:33:09.200 --> 00:33:14.640
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.

00:33:14.640 --> 00:33:21.180
So they don't have to like rely on some kind of external routers to route between different versions.

00:33:21.180 --> 00:33:25.520
So they can keep two versions in same code base and don't have any problems with it.

00:33:25.520 --> 00:33:36.860
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?

00:33:36.860 --> 00:33:43.260
But I mean, I guess you could always add slash V2 slash V3 afterwards.

00:33:43.260 --> 00:33:49.160
But if people think ahead a little bit and put versioning in there explicitly, that would be nice.

00:33:49.540 --> 00:33:59.240
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?

00:33:59.240 --> 00:34:02.220
Or do I have to kind of do some redirect, indirection?

00:34:02.220 --> 00:34:04.520
That part is probably will not work.

00:34:04.520 --> 00:34:07.360
And I think like, so probably someone next releases.

00:34:07.360 --> 00:34:11.280
So inside API, you can also define a couple of routers.

00:34:11.280 --> 00:34:11.560
Yeah.

00:34:11.560 --> 00:34:17.200
So this is kind of, so you can split your application API into like different modules, which called routers.

00:34:17.760 --> 00:34:25.900
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.

00:34:25.900 --> 00:34:34.560
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.

00:34:34.560 --> 00:34:46.800
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.

00:34:46.980 --> 00:34:47.660
Yeah, I really like it.

00:34:47.660 --> 00:34:48.060
That's great.

00:34:48.060 --> 00:34:50.220
Well, yeah, this is that this is an amazing feature.

00:34:50.220 --> 00:34:54.720
When I first saw this, I thought, oh, yeah, this makes version in your API so simple.

00:34:54.720 --> 00:34:59.220
And you even get documentation with it just because that's how documentation works, right?

00:34:59.220 --> 00:34:59.440
Yep.

00:34:59.440 --> 00:35:04.700
And you pointed out that you can have public and private APIs in this similar way.

00:35:04.960 --> 00:35:13.940
And so you could have public APIs, maybe multiple versions of them, but also a separate private API that potentially exposes more more things.

00:35:13.940 --> 00:35:14.200
Right.

00:35:14.200 --> 00:35:17.460
And as you said, you could even put authentication on top of it separately.

00:35:17.460 --> 00:35:17.780
Right.

00:35:17.780 --> 00:35:18.040
Yep.

00:35:18.100 --> 00:35:18.440
I love it.

00:35:18.440 --> 00:35:20.440
Let me go back to the homepage.

00:35:20.440 --> 00:35:21.560
Let me ask you a question here.

00:35:21.560 --> 00:35:24.180
So you said this is another thing that I think I'm a big fan of.

00:35:24.180 --> 00:35:31.600
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.

00:35:31.600 --> 00:35:38.060
But like FastAPI, you can define a Pydantic model that is the type of the thing.

00:35:38.060 --> 00:35:43.480
And then the values of that Pydantic model will be validated and populated from different areas.

00:35:43.480 --> 00:35:43.760
Right.

00:35:43.760 --> 00:35:51.640
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.

00:35:51.640 --> 00:35:51.940
Right.

00:35:51.940 --> 00:35:52.680
This is really key.

00:35:52.680 --> 00:35:53.980
So you define it once.

00:35:53.980 --> 00:35:54.520
Yeah.

00:35:54.520 --> 00:35:56.880
Or maybe you can even define it on a model level.

00:35:56.880 --> 00:36:03.440
So you create your table, you know, customer name is string, you know, birth date is like daytime.

00:36:03.440 --> 00:36:08.780
Then it can be automatically transferred to the schema, which is Pydantic model under the hood.

00:36:08.780 --> 00:36:11.040
And then you call it pass it as an argument.

00:36:11.040 --> 00:36:14.040
And yeah, you have your data validated.

00:36:14.040 --> 00:36:18.380
It automatically parsed through JSON, extracted, validated.

00:36:18.380 --> 00:36:21.120
And you're not only limited to JSON.

00:36:21.120 --> 00:36:25.280
So Django Ninja also comes with what is called parsers.

00:36:25.660 --> 00:36:30.480
So you can define, okay, is it a YAML parser, XML parser, CSV parser.

00:36:30.480 --> 00:36:32.540
So you can define your own parsers.

00:36:32.540 --> 00:36:34.780
And then you can just have different inputs.

00:36:34.780 --> 00:36:36.240
And yeah, they will parse it.

00:36:36.240 --> 00:36:44.280
But yeah, like the core guarantee you have is that your annotated argument that you pass here will always have your log.

00:36:44.280 --> 00:36:46.360
If you have name as a string, it will be string.

00:36:46.360 --> 00:36:49.920
If your birth date is date, then it will be date.

00:36:49.920 --> 00:36:55.700
And you have this kind of guarantee that you can always rely and be sure that it's parsed and validated.

00:36:55.700 --> 00:36:57.060
Yeah, that's a big point of this, right?

00:36:57.120 --> 00:37:01.560
Once the function gets to where your code actually runs, it's good.

00:37:01.560 --> 00:37:03.060
The data has already been validated.

00:37:03.060 --> 00:37:04.480
Everything, right?

00:37:04.480 --> 00:37:06.240
Yeah, you focus on business logic.

00:37:06.240 --> 00:37:10.560
You put aside your mental context about validating stuff.

00:37:10.560 --> 00:37:11.960
You're like it's somewhere else.

00:37:11.960 --> 00:37:13.560
Someone else defined it.

00:37:13.560 --> 00:37:14.840
So you don't worry about it.

00:37:14.940 --> 00:37:18.880
You just, in your operation, you focus only on business logic and that's it.

00:37:18.880 --> 00:37:19.740
That's super interesting.

00:37:19.740 --> 00:37:28.900
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.

00:37:28.900 --> 00:37:32.880
JSON, path parameters, query parameters, cookies, headers, forms, etc.

00:37:33.140 --> 00:37:36.360
Will it take from multiple locations to fill this out?

00:37:36.360 --> 00:37:46.780
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?

00:37:46.780 --> 00:37:48.380
Will it combine it in this way?

00:37:48.380 --> 00:37:50.980
Yeah, basically, like the rules are pretty simple.

00:37:50.980 --> 00:37:53.960
So you can actually mark.

00:37:53.960 --> 00:37:59.060
So there is like your import from Ninja, like, for example, form or header.

00:37:59.060 --> 00:38:05.740
And then in like in this square containers, you can define, okay, this comes from form, let's say.

00:38:05.740 --> 00:38:10.700
And that way you can tell, okay, it's just, okay, you parse the data only from form data.

00:38:10.700 --> 00:38:13.640
Don't just try to parse it through JSON and stuff like that.

00:38:13.640 --> 00:38:17.180
I guess you can search in documentation, try like searching for forms.

00:38:17.180 --> 00:38:19.660
That will be a good example.

00:38:19.660 --> 00:38:20.900
Form data, yeah.

00:38:21.240 --> 00:38:24.620
So you see, you kind of define, so we import form container.

00:38:24.620 --> 00:38:31.640
You say, okay, username is a string that should come from form and password is also string here that comes from form.

00:38:31.640 --> 00:38:38.580
And that way you can just leverage on default, you know, for multi-pal from data or just default form data.

00:38:38.580 --> 00:38:43.540
And yeah, like simply parse it without JSON, but just the raw form data.

00:38:43.540 --> 00:38:48.600
So you can say that for individual values, like if you're passing in the username or password.

00:38:49.320 --> 00:38:53.900
And you can even do that for your Pydantic models, which is cool.

00:38:53.900 --> 00:39:01.160
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?

00:39:01.160 --> 00:39:01.660
Yeah.

00:39:01.660 --> 00:39:06.640
You must have done some pretty challenging type stuff to get this all to work, right?

00:39:06.640 --> 00:39:07.480
To get it.

00:39:07.480 --> 00:39:09.980
So how do you specify it comes from the form?

00:39:10.360 --> 00:39:15.080
But also that when I say item dot, it gives me lists from items, not from form.

00:39:15.080 --> 00:39:15.580
And right.

00:39:15.580 --> 00:39:18.060
Is this like an annotated thing or how does this work?

00:39:18.060 --> 00:39:22.220
This is actually like, like really kind of hack away.

00:39:22.220 --> 00:39:22.880
Like, yeah.

00:39:22.880 --> 00:39:24.100
How to make it work.

00:39:24.320 --> 00:39:25.820
And then the first, yeah.

00:39:25.820 --> 00:39:28.240
Like editor should do that to complete.

00:39:28.240 --> 00:39:33.180
mypy should not like scream at you and it should validate under the hood.

00:39:33.180 --> 00:39:40.300
So there's like a pretty, pretty like nasty kind of if typing stuff, annotations.

00:39:40.300 --> 00:39:46.300
So it's like really like inside it looks ugly, but outside it's like perfect.

00:39:46.300 --> 00:39:46.580
Yeah.

00:39:46.580 --> 00:39:49.480
So you get like, you just define, okay, it's form from item.

00:39:49.480 --> 00:39:54.360
Your editor works, your serializer works, everything works, but under the hood, it's like pretty,

00:39:54.360 --> 00:39:57.220
you don't want to maintain, maintain this code.

00:39:57.220 --> 00:40:02.620
It's like too much stuff to satisfy all parties, the my pie, the code and the editors.

00:40:02.620 --> 00:40:04.080
That's exactly what I was thinking.

00:40:04.080 --> 00:40:08.760
It looks simple, but I bet it was, I bet it was tough, but it's really nice.

00:40:08.760 --> 00:40:08.940
Yeah.

00:40:08.940 --> 00:40:13.020
It's like the differences between 3.7, 3.8, 3.12.

00:40:13.020 --> 00:40:17.180
Like they're doing, you know, every like year the new Python works and then there is like

00:40:17.180 --> 00:40:18.600
little tweaks here and there.

00:40:18.600 --> 00:40:21.920
So it's like, yeah, it's like the simplest to keep it simple.

00:40:21.920 --> 00:40:25.200
Like it's, yeah, it goes like a lot of testing on, on, on, under the hood.

00:40:25.200 --> 00:40:26.860
That testing is probably pretty hard.

00:40:26.860 --> 00:40:30.920
I would imagine because you've got different consumers, you know, how do you make sure that

00:40:30.920 --> 00:40:35.920
an editor works and that my pie works in the code runs, you know, this is not just maybe

00:40:35.920 --> 00:40:37.460
a unit test, a simple unit test.

00:40:37.460 --> 00:40:37.940
I imagine.

00:40:37.940 --> 00:40:41.740
Well, yeah, I remember when I started like, yeah, like I just, I thought, okay, I'll just

00:40:41.740 --> 00:40:44.380
make a 100% coverage on all Python versions.

00:40:44.380 --> 00:40:49.180
But yeah, that was like really tricky part because yeah, there was like edge cases for, for some

00:40:49.180 --> 00:40:49.820
combinations.

00:40:49.820 --> 00:40:55.260
So if you know, maybe like a Jenga plus Python 3.6 or stuff like that was like really giving

00:40:55.260 --> 00:40:55.960
some headaches.

00:40:56.540 --> 00:41:01.320
But I was at some point lucky that first Sebastian did it for FastAPI.

00:41:01.320 --> 00:41:08.380
So I literally just copied lots of test cases from FastAPI codebases, just a little bit

00:41:08.380 --> 00:41:11.920
adapted, and I already had a pretty extensive test coverage.

00:41:11.920 --> 00:41:16.220
So then I just focused on specific implementations of here and there.

00:41:16.220 --> 00:41:22.820
But yeah, the end goal is really the usage should be really simple.

00:41:22.820 --> 00:41:24.260
So your editor works.

00:41:24.640 --> 00:41:30.120
If you use mypy, you really like types, you have 100% type correction.

00:41:30.120 --> 00:41:36.720
And then on the hood, it does as fast as possible, whatever, like Pydantic Magic,

00:41:36.720 --> 00:41:38.040
media with Rust.

00:41:38.040 --> 00:41:44.440
It also does it as fast as possible, currently possible with current technologies.

00:41:44.440 --> 00:41:48.360
I'm going to go talk through the motivation section because you've got this motivation

00:41:48.360 --> 00:41:53.080
page that I think is probably a really good place for people to start as they're considering

00:41:53.080 --> 00:41:53.320
this.

00:41:53.320 --> 00:41:55.780
There's a lot of interesting pieces here.

00:41:55.780 --> 00:42:01.940
For example, some stuff that we haven't touched on like dependency injection and other really,

00:42:01.940 --> 00:42:09.980
really wild ways to sort of combine code to make it cleaner and more single purpose functions,

00:42:09.980 --> 00:42:10.340
right?

00:42:10.560 --> 00:42:19.180
So we talked about the Django Ninja will take inputs from these various locations, validate them, convert them,

00:42:19.180 --> 00:42:20.180
then pass them in.

00:42:20.180 --> 00:42:22.800
But this example you got here.

00:42:22.800 --> 00:42:29.340
You're using external functions to say get a user and then using that as part of the population,

00:42:29.340 --> 00:42:30.000
the data, I guess.

00:42:30.000 --> 00:42:31.000
Tell us about this.

00:42:31.000 --> 00:42:33.620
Basically, this is how FastAPI works.

00:42:33.620 --> 00:42:37.380
So they really define all like arguments there.

00:42:37.380 --> 00:42:42.240
And if you like, let's say you need a database in your function, like if you're using like SQL

00:42:42.240 --> 00:42:50.240
Alchemist, then you're kind of forced to have this DB engine as dependency or like user as a dependency.

00:42:50.780 --> 00:42:57.340
And because, you know, with Django, you kind of already have it either like you don't have like to access database,

00:42:57.340 --> 00:43:01.400
you just use models dot object and you don't have to worry about.

00:43:01.400 --> 00:43:03.340
So this part is for all the way.

00:43:03.340 --> 00:43:10.460
And as well, like users also like stuff that by Django protocol, you kind of have it on a request.

00:43:10.460 --> 00:43:12.780
Yeah, request that user or request that session.

00:43:12.780 --> 00:43:20.960
There's lots of stuff you already have in kind of like settled way and that people already expect it to be there.

00:43:20.960 --> 00:43:25.640
So that's why like, yeah, like I was wondering, should Django Ninja have dependencies?

00:43:25.640 --> 00:43:27.640
But my vision, it should not.

00:43:27.740 --> 00:43:33.900
It should be like a bit more simple and rely on the current way of work where people are already know,

00:43:33.900 --> 00:43:35.000
well, okay, where's my user?

00:43:35.000 --> 00:43:35.700
Where's my session?

00:43:35.700 --> 00:43:37.900
Where's my cookies extra if I need to?

00:43:37.900 --> 00:43:42.700
So you worry only about like your arguments are the stuff that should be validated,

00:43:42.700 --> 00:43:45.180
not the stuff you depend on.

00:43:45.180 --> 00:43:48.160
Yeah, like, you know, database or like some kind of authentication.

00:43:48.160 --> 00:43:52.340
And yeah, that's why you can also separate the logic.

00:43:52.340 --> 00:43:55.680
So because if you already like entered the execution of the function,

00:43:55.760 --> 00:43:58.620
that means all the process that was before is already done.

00:43:58.620 --> 00:44:00.860
You have guaranteed that if you define authentication,

00:44:00.860 --> 00:44:02.780
that's already done and you have your user.

00:44:02.780 --> 00:44:07.240
If you like relied on some kind of validation, then it's already there.

00:44:07.240 --> 00:44:14.180
So again, your focus is only on business logic of application and the rest pieces should not be involved.

00:44:14.180 --> 00:44:17.040
And don't, you know, you don't bother with them at all.

00:44:17.120 --> 00:44:17.820
This is super nice.

00:44:17.820 --> 00:44:22.840
So let me try to give people a sense of what I'm talking about here over audio.

00:44:22.840 --> 00:44:30.380
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.

00:44:30.380 --> 00:44:32.660
It needs the current user and it needs some arguments.

00:44:32.660 --> 00:44:33.140
Right.

00:44:33.140 --> 00:44:39.080
So the argument is handled by just what comes into the posters, part of the URLs and stuff.

00:44:39.180 --> 00:44:45.420
And then there's a database and you can say this depends on a function, which then Django Ninja will call automatically.

00:44:45.420 --> 00:44:54.980
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.

00:44:54.980 --> 00:44:56.540
This is how FastAPI works.

00:44:56.540 --> 00:45:01.120
So it depends is actually not the part of Django Ninja on purpose.

00:45:01.120 --> 00:45:07.040
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.

00:45:07.120 --> 00:45:10.840
This is the Django, this is the, this is the FastAPI version.

00:45:10.840 --> 00:45:11.160
Yeah.

00:45:11.160 --> 00:45:11.360
Yeah.

00:45:11.360 --> 00:45:13.100
And so you're saying, no, this is what we do instead.

00:45:13.100 --> 00:45:13.440
Okay.

00:45:13.440 --> 00:45:14.000
Got it.

00:45:14.000 --> 00:45:14.640
Yeah.

00:45:14.640 --> 00:45:14.960
You know what?

00:45:14.960 --> 00:45:17.580
I'm not a big fan of dependency injection.

00:45:17.580 --> 00:45:23.400
Not necessarily the arguments are fine, but this, the whole idea of dependency injection, I always find it looks cool in practice.

00:45:23.400 --> 00:45:26.400
But then when I'm trying to work with it, I'm like, where does this come from?

00:45:26.400 --> 00:45:27.660
Like, why do I have this?

00:45:27.660 --> 00:45:29.320
And how do I, how do I get back to it?

00:45:29.320 --> 00:45:36.020
You know, I have kind of the same feeling about like any, any like Python approaches to the dependency injection libraries,

00:45:36.020 --> 00:45:43.520
because I think the Pythonic way is actually like, like it's more kind of, because you can import from strings.

00:45:43.520 --> 00:45:49.640
You can, you know, pass kind of any type of arguments and just access them by like the, this quarks.

00:45:49.640 --> 00:46:07.980
So you, I think you never should need like, you know, Java, Java world dependency injection, because 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.

00:46:07.980 --> 00:46:15.120
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.

00:46:15.120 --> 00:46:24.940
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.

00:46:24.940 --> 00:46:26.540
They could have done like the same way.

00:46:26.540 --> 00:46:33.220
Like, okay, like let's do dependence injection or some kind of SSL protocols, like, you know, timeouts and stuff like that.

00:46:33.220 --> 00:46:42.960
But they, they really try to like make it as current Pythonic way as possible through like some quarks or like, or, or like, you know, stuff that you, you never, you never know.

00:46:42.960 --> 00:46:43.120
Okay.

00:46:43.120 --> 00:46:47.380
What's what's actually injected, but verbally, you know, what, what's, what's going on.

00:46:47.380 --> 00:46:57.800
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.

00:46:57.800 --> 00:47:01.720
The only way to know is to go read the documentation, which not a big fan.

00:47:01.720 --> 00:47:02.980
Let's talk about authentication.

00:47:02.980 --> 00:47:07.100
So what's the Django Ninja way for authentication here?

00:47:07.100 --> 00:47:08.320
It looks pretty integrated.

00:47:08.320 --> 00:47:12.920
It's kind of both integrated and it's pretty like loose and flexible.

00:47:13.120 --> 00:47:19.580
So yeah, even, even, so, you know, you just on your ninja API, you define like auth variable.

00:47:19.580 --> 00:47:23.160
So it's purposely not, not called authorization or authentication.

00:47:23.160 --> 00:47:26.080
It's just auth, you know, so you can define yourself.

00:47:26.080 --> 00:47:28.460
Do you want to implement authorization or authentication?

00:47:28.460 --> 00:47:29.620
It's like really up to you.

00:47:29.620 --> 00:47:29.820
Okay.

00:47:29.820 --> 00:47:34.900
Because it's like, yeah, you just, the auth is like a function that will be called with your request.

00:47:34.900 --> 00:47:42.460
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.

00:47:42.900 --> 00:47:49.740
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.

00:47:49.740 --> 00:47:50.220
Dot.

00:47:50.220 --> 00:47:51.640
Off inside this function.

00:47:51.640 --> 00:47:57.540
You can also access request and like attach your request.user, which is what is in most cases are done.

00:47:57.540 --> 00:48:05.100
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.

00:48:05.100 --> 00:48:06.760
Like it's, it's not always, you have user.

00:48:06.760 --> 00:48:09.760
So maybe you just need some kind of object.

00:48:09.760 --> 00:48:11.600
Maybe you have API keys.

00:48:12.680 --> 00:48:14.000
Client or yeah, whatever.

00:48:14.000 --> 00:48:14.300
Yeah.

00:48:14.300 --> 00:48:14.540
Yeah.

00:48:14.540 --> 00:48:16.100
Maybe you just need to pass an API key.

00:48:16.100 --> 00:48:17.060
It's not tied to someone.

00:48:17.060 --> 00:48:17.560
Yeah, for sure.

00:48:17.560 --> 00:48:22.860
I tried not to like force people into like deciding what exactly they're implementing.

00:48:22.860 --> 00:48:26.080
They just need to implement a function and then use it.

00:48:26.080 --> 00:48:35.360
But yeah, by default, you can just import default Django auth that will use like your standard Django session based authentication.

00:48:35.540 --> 00:48:39.920
And yeah, just live with that by some default cookies, tokens.

00:48:39.920 --> 00:48:41.060
So like whatever you prefer.

00:48:41.060 --> 00:48:41.960
What about async?

00:48:41.960 --> 00:48:44.340
I think it was like one of the core features.

00:48:44.340 --> 00:48:45.340
Yeah.

00:48:45.340 --> 00:48:48.960
Like, and what was like different from Django rest framework.

00:48:48.960 --> 00:48:50.840
And yeah, it kind of, yeah, it does.

00:48:50.840 --> 00:49:02.220
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.

00:49:02.220 --> 00:49:03.220
Yeah.

00:49:03.220 --> 00:49:08.400
But yeah, the thing is like you're not, you are not required to use one or another.

00:49:08.400 --> 00:49:12.220
So you can pretty much mix both worlds in your, in your code.

00:49:12.220 --> 00:49:18.600
So, because, you know, it was Django because initially Django was not designed to be, I think, friendly.

00:49:18.600 --> 00:49:31.600
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.

00:49:31.600 --> 00:49:38.600
So this is, this part is like, it's really hidden from you and you cannot really sometimes be involved in manipulating.

00:49:38.600 --> 00:49:42.900
And so that, that request also gets into async context.

00:49:42.900 --> 00:49:51.100
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.

00:49:51.100 --> 00:49:52.520
They decide what you need.

00:49:52.520 --> 00:50:00.980
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.

00:50:00.980 --> 00:50:01.980
Simple, easy.

00:50:01.980 --> 00:50:05.980
And you just don't worry about if it's, you know, that does some kind of a little bit of weight.

00:50:05.980 --> 00:50:13.980
And so mom, I would say my, my go to approach is like, yeah, basically I do all the stuff in sync mode.

00:50:13.980 --> 00:50:22.980
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.

00:50:22.980 --> 00:50:31.980
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.

00:50:31.980 --> 00:50:35.980
But by default, I would, I would just go with no, no, no, no, no, no, no.

00:50:35.980 --> 00:50:36.980
I think way.

00:50:36.980 --> 00:50:37.980
Yeah. I would as well.

00:50:37.980 --> 00:50:45.980
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.

00:50:45.980 --> 00:50:48.980
So, you know, work your way back and make the view function async as well.

00:50:48.980 --> 00:50:50.980
But if you don't have to, then certainly don't, right?

00:50:50.980 --> 00:50:53.980
It just adds complexity that you don't necessarily need.

00:50:53.980 --> 00:50:58.980
The reason I'm asking is what's the story with the Django ORM in async these days.

00:50:58.980 --> 00:51:03.980
I know that this is kind of one of the last parts for the Django async support journey.

00:51:03.980 --> 00:51:05.980
I think they settled on what it is now.

00:51:05.980 --> 00:51:12.980
So you can like, there's extra methods on ORM, like a get, a create, a like filter or stuff like that.

00:51:12.980 --> 00:51:16.980
So you can already like do a bunch of default stuff.

00:51:16.980 --> 00:51:18.980
All right. So those you can await already. And that should be.

00:51:18.980 --> 00:51:31.980
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.

00:51:31.980 --> 00:51:35.980
So then you need to like, keep in mind that you need to like do, do the join.

00:51:35.980 --> 00:51:42.980
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.

00:51:42.980 --> 00:51:53.980
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.

00:51:53.980 --> 00:51:58.980
So yeah, they, let's say they're creating like schema that outputs user and a group.

00:51:58.980 --> 00:52:00.980
Yeah. Like again, a group is some kind of schema.

00:52:00.980 --> 00:52:05.980
So they just, okay, I'll just return the async query set and yeah, be, be ready with it.

00:52:05.980 --> 00:52:13.980
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.

00:52:13.980 --> 00:52:14.980
So you kind of get an error.

00:52:14.980 --> 00:52:28.980
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.

00:52:28.980 --> 00:52:31.980
So that's good advice for people, even if they're not doing async, right?

00:52:31.980 --> 00:52:32.980
Right.

00:52:32.980 --> 00:52:34.980
This, this N plus one problem can make your ORMs very slow.

00:52:34.980 --> 00:52:36.980
And if you know, I'm going to go access this thing.

00:52:36.980 --> 00:52:37.980
That's a foreign key.

00:52:37.980 --> 00:52:44.980
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.

00:52:44.980 --> 00:52:49.980
And then you kind of, they will bite you before you go to production.

00:52:49.980 --> 00:52:51.980
So maybe that's, that's the way to go.

00:52:51.980 --> 00:52:53.980
Some people will like this and some people will not like it.

00:52:53.980 --> 00:52:55.980
But yeah, it will definitely let you know what's going on.

00:52:55.980 --> 00:52:56.980
Right.

00:52:56.980 --> 00:52:57.980
Amazing.

00:52:57.980 --> 00:52:58.980
Anything else before?

00:52:58.980 --> 00:53:06.980
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?

00:53:06.980 --> 00:53:10.980
Or do I have to run with an async server separately?

00:53:10.980 --> 00:53:11.980
What's the story there?

00:53:11.980 --> 00:53:12.980
It's just work out of the box.

00:53:12.980 --> 00:53:20.980
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.

00:53:20.980 --> 00:53:23.980
So basically it's like, it's out of the box.

00:53:23.980 --> 00:53:24.980
It should work.

00:53:24.980 --> 00:53:29.980
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.

00:53:29.980 --> 00:53:34.980
So this is where you will have to keep, keep, keep attention and make sure it's like a weighted properly.

00:53:34.980 --> 00:53:35.980
Yeah.

00:53:35.980 --> 00:53:36.980
UVicorn is really nice.

00:53:36.980 --> 00:53:37.980
They're making a lot of progress.

00:53:37.980 --> 00:53:39.980
They're now a standalone server.

00:53:39.980 --> 00:53:40.980
You don't have to use them.

00:53:40.980 --> 00:53:43.980
You don't have to say G unicorn with UVicorn workers.

00:53:43.980 --> 00:53:47.980
You can just run it as its own proper Linux server and so on.

00:53:47.980 --> 00:53:48.980
It's really nice.

00:53:48.980 --> 00:53:49.980
All right.

00:53:49.980 --> 00:53:50.980
So that's what brings up.

00:53:50.980 --> 00:53:53.980
Is there anything else you want to tell people about Django Ninja that we, we should cover?

00:53:53.980 --> 00:54:03.980
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.

00:54:03.980 --> 00:54:10.980
They just simply ask your, you know, favorite open AI or anthropic chat to like write you some code.

00:54:10.980 --> 00:54:18.980
So what, what I currently notice is like, you know, when library do some kind of new iterations, you're like introduce new stuff.

00:54:18.980 --> 00:54:21.980
Like, you know, make some kind of, you know, maybe even breaking changes.

00:54:21.980 --> 00:54:26.980
You know, the LLMs will not sexually be out time with, with those changes.

00:54:26.980 --> 00:54:28.980
And that is actually like bringing lots of problems.

00:54:28.980 --> 00:54:42.980
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.

00:54:42.980 --> 00:54:55.980
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.

00:54:55.980 --> 00:54:55.980
Yeah.

00:54:55.980 --> 00:55:04.980
Like whatever new version, like I saw this a lot with Pydantic when they, you know, they already like a version two for like a year in a while.

00:55:04.980 --> 00:55:09.980
While if you ask LLM to implement some stuff, they, they still giving you the old, old approaches.

00:55:09.980 --> 00:55:17.980
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.

00:55:17.980 --> 00:55:29.980
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 latest, latest result.

00:55:29.980 --> 00:55:30.980
Oh, that'd be cool.

00:55:30.980 --> 00:55:31.980
Yeah.

00:55:31.980 --> 00:55:36.980
But if you like listening to this podcast, you can go to LLM.jangoNinja.dev.

00:55:36.980 --> 00:55:40.980
It will, it will, we'll redirect you to some ticket where I will notify when it's ready.

00:55:40.980 --> 00:55:45.980
So like for now, we just redirect to, to GitHub issue.

00:55:45.980 --> 00:55:57.980
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.

00:55:57.980 --> 00:56:08.980
And it will answer you latest, latest and better approaches without like, you know, forgetting all the old knowledge and giving you the newer updated version.

00:56:08.980 --> 00:56:14.980
So that's, this is a serious annoyance with LLMs because you'll ask it a question on how to do this thing.

00:56:14.980 --> 00:56:15.980
And it looks like an amazing answer.

00:56:15.980 --> 00:56:17.980
You're like, Oh my gosh, save me so much time.

00:56:17.980 --> 00:56:22.980
And then you'll try it and it'll, it'll be using a function or a class or something that doesn't exist.

00:56:22.980 --> 00:56:24.980
Like, Oh, you're lying to me, please.

00:56:24.980 --> 00:56:25.980
This doesn't exist.

00:56:25.980 --> 00:56:26.980
Oh, you're right.

00:56:26.980 --> 00:56:27.980
Here's the real way.

00:56:27.980 --> 00:56:31.980
And that's, that's even before you get to it being trained exactly on the docs.

00:56:31.980 --> 00:56:36.980
So are you using some sort of rag local LLM or what's, what are you doing here?

00:56:36.980 --> 00:56:38.980
Generally, like my vision is like, yeah.

00:56:38.980 --> 00:56:48.980
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.

00:56:48.980 --> 00:56:49.980
Yeah.

00:56:49.980 --> 00:57:03.980
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.

00:57:03.980 --> 00:57:08.980
And it's, it's getting like this kind of boost that you don't really want to.

00:57:08.980 --> 00:57:14.980
And this is like, I think it becomes kind of problematic that, you know, that you cannot really suggest to large language models.

00:57:14.980 --> 00:57:15.980
Okay.

00:57:15.980 --> 00:57:19.980
What's the recommended way and why it's better than, than it's currently like suggested.

00:57:19.980 --> 00:57:26.980
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.

00:57:26.980 --> 00:57:34.980
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.

00:57:34.980 --> 00:57:42.980
So that, you know, users, users always use the, how it was designed at the beginning and yeah, like never, never feel any differences.

00:57:42.980 --> 00:57:43.980
Easier said than done.

00:57:43.980 --> 00:57:46.980
I will add though, that your documentation has really nice search.

00:57:46.980 --> 00:57:53.980
So that that's easy to go here and, and even without the LLM it's pretty approachable.

00:57:53.980 --> 00:57:58.980
So it's not, you know, I don't feel like you have to use LLMs to figure this out.

00:57:58.980 --> 00:57:59.980
I'm sure people will.

00:57:59.980 --> 00:58:04.980
That's actually also the goal, like how, how you make it so that people don't, don't, don't ask any question.

00:58:04.980 --> 00:58:06.980
Don't Google like it should be like intuitive.

00:58:06.980 --> 00:58:13.980
Let's wrap it up with a thought, I guess, but we talked about using Python typing as a core element of the framework.

00:58:13.980 --> 00:58:21.980
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.

00:58:21.980 --> 00:58:29.980
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.

00:58:29.980 --> 00:58:30.980
You go, well, this must be the one.

00:58:30.980 --> 00:58:33.980
And you know, it guides you down the path.

00:58:33.980 --> 00:58:41.980
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.

00:58:41.980 --> 00:58:41.980
Yeah.

00:58:41.980 --> 00:58:42.980
So I think it's very approachable.

00:58:42.980 --> 00:58:44.980
Even if you use some, some stuff like a pilot.

00:58:44.980 --> 00:58:45.980
Yeah.

00:58:45.980 --> 00:58:56.980
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.

00:58:56.980 --> 00:58:58.980
And yeah, it's makes you laugh easier.

00:58:58.980 --> 00:58:59.980
Yeah, it absolutely does.

00:58:59.980 --> 00:59:00.980
All right.

00:59:00.980 --> 00:59:01.980
Final thoughts.

00:59:01.980 --> 00:59:03.980
People wouldn't get started with Django Ninja.

00:59:03.980 --> 00:59:04.980
What do you tell them?

00:59:04.980 --> 00:59:15.980
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.

00:59:15.980 --> 00:59:16.980
So what should I try?

00:59:16.980 --> 00:59:22.980
And you're like, there's always like people who answer a bunch of stuff and pros and cons.

00:59:22.980 --> 00:59:33.980
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.

00:59:33.980 --> 00:59:44.980
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.

00:59:44.980 --> 00:59:45.980
I don't count.

00:59:45.980 --> 00:59:56.980
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.

00:59:56.980 --> 00:59:58.980
So yeah, should not, not get worried.

00:59:58.980 --> 01:00:11.980
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.

01:00:11.980 --> 01:00:15.980
Try to make it for people to migrate to newer, newer versions easier.

01:00:15.980 --> 01:00:26.980
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, Pydantic moved from one to two.

01:00:26.980 --> 01:00:30.980
So like, there was no way to make it without breaking changes.

01:00:30.980 --> 01:00:42.980
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.

01:00:42.980 --> 01:00:44.980
So yeah, that's my, that's my goal.

01:00:44.980 --> 01:00:46.980
And yeah, this is why you should try it.

01:00:46.980 --> 01:00:54.980
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.

01:00:54.980 --> 01:01:01.980
And I think like one more thing that we didn't talk about, but you should definitely try is, is project called nano Django.

01:01:01.980 --> 01:01:08.980
So this is a way for you to write a code, like a pro Django project, but just in one file.

01:01:08.980 --> 01:01:11.980
So under the hood, it also uses Django Ninja.

01:01:11.980 --> 01:01:18.980
So this is also like a very, very easy thing to be, to join the Django Ninja tryout thing.

01:01:18.980 --> 01:01:20.980
So here in one file, you can define your model.

01:01:20.980 --> 01:01:24.980
You can define your API, run it with one command and you're ready to go.

01:01:24.980 --> 01:01:29.980
And basically like same, same way you used to work with Flask or FastAPI.

01:01:29.980 --> 01:01:38.980
But the coolest thing about this is like when you see, okay, your, your file grow to like, you know, hundreds of thousands of lines.

01:01:38.980 --> 01:01:45.980
There is a tooling that you just, it can convert for you, like your single file into a standard Django project structure.

01:01:45.980 --> 01:01:54.980
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,

01:01:54.980 --> 01:01:56.980
process and all the conventions.

01:01:56.980 --> 01:01:59.980
And yeah, you continue your project without, without any,

01:01:59.980 --> 01:02:00.980
Yeah.

01:02:00.980 --> 01:02:01.980
Yeah.

01:02:01.980 --> 01:02:03.980
Nano Django is an interesting project for sure.

01:02:03.980 --> 01:02:09.980
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.

01:02:09.980 --> 01:02:11.980
This is like a very first way to try that to do.

01:02:11.980 --> 01:02:18.980
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.

01:02:18.980 --> 01:02:19.980
Cool.

01:02:19.980 --> 01:02:20.980
Awesome.

01:02:20.980 --> 01:02:21.980
I'll definitely put that in the links for people as well.

01:02:21.980 --> 01:02:23.980
Well, Vitaly, thank you for being here.

01:02:23.980 --> 01:02:29.980
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.

01:02:29.980 --> 01:02:33.980
I know it's a ridiculous time and be safe out there.

01:02:33.980 --> 01:02:34.980
You guys.

01:02:34.980 --> 01:02:34.980
Thank you.

01:02:34.980 --> 01:02:35.980
Thank you for being here.

01:02:35.980 --> 01:02:38.980
Thank you for creating this awesome project and coming on the show and sharing it with us.

01:02:38.980 --> 01:02:39.980
Thank you for having me.

01:02:39.980 --> 01:02:40.980
Thank you very much.

01:02:40.980 --> 01:02:41.980
Yeah.

01:02:41.980 --> 01:02:42.980
Bye.

01:02:42.980 --> 01:02:42.980
Bye.

01:02:42.980 --> 01:02:44.980
This has been another episode of talk Python to me.

01:02:44.980 --> 01:02:46.980
Thank you to our sponsors.

01:02:46.980 --> 01:02:47.980
Be sure to check out what they're offering.

01:02:47.980 --> 01:02:49.980
It really helps support the show.

01:02:49.980 --> 01:02:51.980
Take some stress out of your life.

01:02:51.980 --> 01:02:57.980
Get notified immediately about errors and performance issues in your web or mobile applications with Sentry.

01:02:57.980 --> 01:03:02.980
Just visit talkpython.fm/sentry and get started for free.

01:03:02.980 --> 01:03:06.980
And be sure to use the promo code talkpython, all one word.

01:03:06.980 --> 01:03:08.980
And this episode is brought to you by Bluehost.

01:03:08.980 --> 01:03:10.980
Do you need a website fast?

01:03:10.980 --> 01:03:11.980
Get Bluehost.

01:03:11.980 --> 01:03:16.980
Their AI builds your WordPress site in minutes and their built-in tools optimize your growth.

01:03:16.980 --> 01:03:17.980
Don't wait.

01:03:17.980 --> 01:03:21.980
Visit talkpython.fm/bluehost to get started.

01:03:21.980 --> 01:03:22.980
Want to level up your Python?

01:03:22.980 --> 01:03:26.980
We have one of the largest catalogs of Python video courses over at Talk Python.

01:03:26.980 --> 01:03:31.980
Our content ranges from true beginners to deeply advanced topics like memory and async.

01:03:31.980 --> 01:03:33.980
And best of all, there's not a subscription in sight.

01:03:33.980 --> 01:03:36.980
Check it out for yourself at training.talkpython.fm.

01:03:36.980 --> 01:03:39.980
Be sure to subscribe to the show.

01:03:39.980 --> 01:03:41.980
Open your favorite podcast app and search for Python.

01:03:41.980 --> 01:03:43.980
We should be right at the top.

01:03:43.980 --> 01:03:52.980
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.

01:03:52.980 --> 01:03:55.980
We're live streaming most of our recordings these days.

01:03:55.980 --> 01:04:02.980
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.

01:04:02.980 --> 01:04:05.980
This is your host, Michael Kennedy.

01:04:05.980 --> 01:04:06.980
Thanks so much for listening.

01:04:06.980 --> 01:04:07.980
I really appreciate it.

01:04:07.980 --> 01:04:09.980
Now get out there and write some Python code.

01:04:09.980 --> 01:04:10.860
write some Python code.

01:04:10.860 --> 01:04:13.860
Open arms!

01:04:13.860 --> 01:04:15.860
Open arms!

01:04:15.860 --> 01:04:17.860
Open arms!

01:04:17.860 --> 01:04:19.860
Open arms!

01:04:19.860 --> 01:04:21.860
Open arms!

01:04:21.860 --> 01:04:23.860
Open arms!

01:04:23.860 --> 01:04:25.860
Open arms!

01:04:25.860 --> 01:04:27.860
Open arms!

01:04:27.860 --> 01:04:28.360
you

01:04:28.360 --> 01:04:28.860
you

01:04:28.860 --> 01:04:29.860
Thank you.

01:04:29.860 --> 01:04:31.860
Thank you.

