WEBVTT

00:00:00.001 --> 00:00:05.420
Two frameworks that have taken the Python world by storm lately are FastAPI and Pydantic.

00:00:05.420 --> 00:00:10.560
Once you already have your data exchange model in Pydantic, you might want to use that code for

00:00:10.560 --> 00:00:15.700
storing or talking to your database. And if you have database models, you might want to somehow

00:00:15.700 --> 00:00:21.380
use those models to power and document the APIs you're already building with FastAPI.

00:00:21.380 --> 00:00:27.560
But the popular ORMs, such as SQLAlchemy and others, far predate Pydantic. But could those

00:00:27.560 --> 00:00:33.260
two things be put together? Sebastian Ramirez is here to tell us the answer is yes. We're

00:00:33.260 --> 00:00:37.640
covering his project SQL model, which is the marriage between Pydantic and SQLAlchemy.

00:00:37.640 --> 00:00:43.780
This is Talk Python2Me, episode 353, recorded January 17th, 2022.

00:00:56.840 --> 00:01:01.720
Welcome to Talk Python2Me, a weekly podcast on Python. This is your host, Michael Kennedy.

00:01:01.720 --> 00:01:06.380
Follow me on Twitter where I'm @mkennedy and keep up with the show and listen to past episodes

00:01:06.380 --> 00:01:12.900
at talkpython.fm. And follow the show on Twitter via at Talk Python. We've started streaming most of

00:01:12.900 --> 00:01:18.360
our episodes live on YouTube. Subscribe to our YouTube channel over at talkpython.fm/youtube

00:01:18.360 --> 00:01:23.980
to get notified about upcoming shows and be part of that episode. This episode is brought to you by

00:01:23.980 --> 00:01:29.200
datadog and tonic.ai. Please check out what they're both offering during their segments.

00:01:29.200 --> 00:01:33.520
It really helps support the show. Transcripts for this episode are brought to you by Assembly AI.

00:01:33.520 --> 00:01:39.840
Their speech to text APIs automatically transcribe and understand audio and video in just a few lines

00:01:39.840 --> 00:01:43.860
of Python code. Check them out at talkpython.fm/assemblyai.

00:01:45.060 --> 00:01:47.220
Sebastian, welcome back to Talk Python2Me.

00:01:47.220 --> 00:01:50.420
Thank you very much. Thank you for inviting me. It's a pleasure to be here again.

00:01:50.420 --> 00:01:54.960
It's great to have you back. When you were on the show, we were talking about FastAPI,

00:01:54.960 --> 00:01:59.580
and it seemed like so much had happened. You've done so much, and now there's like all these other

00:01:59.580 --> 00:02:02.860
frameworks that you've built and all sorts of exciting things, right?

00:02:02.860 --> 00:02:08.240
Yeah. Very, very exciting stuff. It's like very exciting because Python is getting so excited.

00:02:08.320 --> 00:02:13.160
Well, it's very exciting. It has always been exciting, but there's so many new things that

00:02:13.160 --> 00:02:15.000
it's great to build things on top of.

00:02:15.000 --> 00:02:18.060
It's interesting. I feel like your frameworks, more than many,

00:02:18.060 --> 00:02:22.340
take advantage of and almost depend upon the latest aspects of Python.

00:02:22.340 --> 00:02:29.160
Yeah, yeah, absolutely. Like, someone made a meme at some point on Twitter about, like,

00:02:29.160 --> 00:02:35.340
this guy that was like, pen, pineapple, apple pen. And it was like, type annotations and another

00:02:35.340 --> 00:02:39.600
library and just put it together. And that's what I'm building. And yeah, it's pretty accurate.

00:02:39.600 --> 00:02:46.580
Yeah, absolutely. You know, there's, as a community, we sort of muddled our way through the Python 2 to 3

00:02:46.580 --> 00:02:52.860
transition, and it took a lot longer than even Guido and everyone, many other people expected it to

00:02:52.860 --> 00:02:57.660
take. But now that we're on the other side of it, stuff like what you're creating and other people are

00:02:57.660 --> 00:03:03.000
creating, that's what would have been possible had we gone sooner, right? But now it's like,

00:03:03.000 --> 00:03:07.720
no, everyone's putting their effort into this, these new ideas and these new aspects that are now

00:03:07.720 --> 00:03:08.200
possible.

00:03:08.200 --> 00:03:09.100
Yeah, yeah.

00:03:09.100 --> 00:03:09.680
I think it's great.

00:03:09.680 --> 00:03:17.360
Yeah, absolutely. And I feel like the way Python is growing and improving is amazing. Like, you know,

00:03:17.360 --> 00:03:22.820
there are some growing pains, like as with any project or with anything, but like, so like,

00:03:22.820 --> 00:03:27.640
it's been able to grow in the directions that are needed and to support all the things that's

00:03:27.640 --> 00:03:32.840
what users are needing. And like, we can do very cool stuff that will not even be possible

00:03:32.840 --> 00:03:35.640
in other languages. And it's, I don't know, for me, it's pretty exciting.

00:03:35.640 --> 00:03:41.020
Yeah. It's the same as for me as well. It just gets more exciting. You know, you could see it,

00:03:41.020 --> 00:03:44.280
you just keep working the same thing. And it just keeps, ah, well, I've been doing that for a long

00:03:44.280 --> 00:03:48.360
time. I need to change, but I don't feel that way at all. I feel like every day there's something new

00:03:48.360 --> 00:03:54.060
and amazing and still the possibility for more incredible things to come is certainly out there.

00:03:54.060 --> 00:03:59.620
I don't feel like we've hit the limit of what it's possible for framework authors like you to build

00:03:59.620 --> 00:04:05.080
or for the core devs to make Python do, you know, there's the whole performance resurgence thing

00:04:05.080 --> 00:04:11.300
that Guido Van Rossum and Mark Shannon are doing, that Sam Gill did, that Anthony Shaw is doing,

00:04:11.300 --> 00:04:14.380
and others. Yeah. I think there's, it's good, right?

00:04:14.380 --> 00:04:20.660
Yeah. It's amazing. And I feel like the, how the energy between the community and the

00:04:20.660 --> 00:04:27.120
core developers and like editors and all the tooling, like all growing together and all supporting each

00:04:27.120 --> 00:04:32.860
other, like helps each other part to grow more and better. And it's so exciting, you know,

00:04:32.860 --> 00:04:38.380
to get like, for example, like support for very recent things and have been able to use them

00:04:38.380 --> 00:04:42.420
right away in editors. It's like, oh, so cool. It is absolutely cool. And yeah,

00:04:42.420 --> 00:04:47.660
the editors are definitely coming along as well. Now, before we get into your latest project,

00:04:47.660 --> 00:04:53.400
SQL model, which is very exciting, let's just get a quick update on you. You know,

00:04:53.400 --> 00:04:57.060
you told us the story, how you got into programming Python before, so we're not going to ask you that

00:04:57.060 --> 00:05:01.640
again, but what have you been up to since you were on the show last? So when I was on the show last,

00:05:01.640 --> 00:05:06.740
we were talking about FastAPI, right? And the cyber already existed if I'm wrong, right?

00:05:06.740 --> 00:05:10.320
Yeah. I think that those were the two things you had built and FastAPI was...

00:05:10.320 --> 00:05:11.380
Okay. Okay. Yeah.

00:05:11.380 --> 00:05:16.460
Had FastAPI made it to the top three web frameworks yet? I'm not sure if it had, but it was,

00:05:16.460 --> 00:05:18.640
it was right around that time. Yeah. That's incredible.

00:05:18.640 --> 00:05:25.580
I think it was very close to that point. That was mind blowing that people were being able to use it so

00:05:25.580 --> 00:05:31.160
much and adopted so much. And like when it came out, out in the service like that, it was super cool.

00:05:31.160 --> 00:05:35.520
Yeah. It was, yeah, super exciting. And yeah, like, I don't know, like I have been, you know,

00:05:35.520 --> 00:05:40.760
like I have been trying to focus, like I have always been trying to focus on whatever is the next

00:05:40.760 --> 00:05:47.220
thing that I can work on that will have the biggest deep pipe that can the most. And I ended up just like

00:05:47.220 --> 00:05:52.460
changing areas and like trying to improve different areas and different things. And recently that I was

00:05:52.460 --> 00:05:58.240
working with the SQL databases, well, or recently, I don't know, some on-sable, that I was working

00:05:58.240 --> 00:06:02.940
with SQL databases and I was working some of the existing libraries and I wanted to have like all

00:06:02.940 --> 00:06:09.360
the benefits of the new features of Python, but I wasn't able to have like as much things as I could

00:06:09.360 --> 00:06:14.300
because most of these libraries were built before we had all these, all these new features.

00:06:14.300 --> 00:06:20.700
So I wanted to be able to get that. And I figured that the best way to build it was applying the ideas

00:06:20.700 --> 00:06:24.920
that I had and the learnings that I had from the other tools and from the other things that just like

00:06:24.920 --> 00:06:30.380
put the thing together because there were some libraries that were trying to do similar things, but like

00:06:30.380 --> 00:06:35.380
I felt like there was still a bit more that could be done. So yeah, I was just like trying to get that.

00:06:35.380 --> 00:06:37.480
And that's how we ended up starting.

00:06:37.480 --> 00:06:43.920
Yeah. Fantastic. Now I feel like SQL model, I don't know for sure I'm asking you, but it seems to me

00:06:43.920 --> 00:06:50.320
looking in from the outside that SQL model was something you're like, I need a good ORM

00:06:50.320 --> 00:06:55.840
or FastAPI. And it, the things out there didn't click for you in the way that you wanted it. So

00:06:55.840 --> 00:06:58.640
you're like, I'm going to build something that fits with this. Right.

00:06:58.640 --> 00:07:04.720
The good thing with FastAPI is that it doesn't have any need for tightly coupling it with any ORM,

00:07:04.720 --> 00:07:11.040
with any database. So it can be used with anything, but still there are some, some things that might not

00:07:11.040 --> 00:07:17.440
be as convenient in the ORM itself. Like if you use it alone. And for example, with FastAPI, then you use it to

00:07:17.440 --> 00:07:22.720
declare all the data models, all the shapes of the data that you want to receive and you want to send

00:07:22.720 --> 00:07:26.880
back and to do like all the data validation, documentation, serialization. Then you declare

00:07:26.880 --> 00:07:31.760
a bunch of those data models with Pydantic. But at the same time, you will end up declaring,

00:07:31.760 --> 00:07:37.840
duplicating a lot of that information in a separate ORM just to connect to the database and to handle the

00:07:37.840 --> 00:07:43.120
database and stuff with Python objects. But then you have to duplicate the information in two different ways.

00:07:43.120 --> 00:07:48.720
And that's what was like, it was not the best developer experience, I guess. And I was trying to

00:07:48.720 --> 00:07:56.800
make it a bit more user friendly, a bit more developer friendly, I guess, to work with databases and data

00:07:56.800 --> 00:08:02.560
models and avoid all that duplication information. And at the same time, make it as easy as possible

00:08:02.560 --> 00:08:07.120
to write a code, just using the same standard type annotations and just using the same, the same

00:08:07.120 --> 00:08:12.160
intuitive things that we can already use. And that's the point that I was trying to hit.

00:08:12.160 --> 00:08:18.960
For people to see how that clicks together, I know 12% of the community who builds web APIs and

00:08:18.960 --> 00:08:24.640
frameworks is using FastAPI, but there's a decent percent out there who maybe haven't heard or

00:08:24.640 --> 00:08:30.240
looked into FastAPI. Maybe they've heard about it, but they don't know the pieces. Maybe given that

00:08:30.240 --> 00:08:36.640
that's some of the motivation, maybe give us a sense of how do you build data models that match your APIs

00:08:36.640 --> 00:08:42.240
and how do you do things like generate the open, the swagger documentation and stuff like that, like

00:08:42.240 --> 00:08:49.680
set the stage for why just straight SQLAlchemy or some other standard pony ORM or something like that

00:08:49.680 --> 00:08:57.040
didn't just directly map over to how FastAPI works. Awesome. So like just a very quick intro to FastAPI,

00:08:57.040 --> 00:09:02.560
it's a web framework that is focused a lot on building web APIs. The main idea is that using

00:09:02.560 --> 00:09:07.840
the standard type annotations or type hints, so the way that you in a function you declare what is the

00:09:07.840 --> 00:09:13.120
type of some particular variable, using that same information, that same information by default will

00:09:13.120 --> 00:09:18.560
give you some certainty that the code is correct and will give you the completion and the inline errors

00:09:18.560 --> 00:09:24.640
in the edit. FastAPI uses that same information to do data validation of the data that you receive in

00:09:24.640 --> 00:09:30.560
the web API and to do the serialization of the data that you are returning back and to do automatic

00:09:30.560 --> 00:09:35.200
documentation. This is all based on a bunch of standards, open API, JSON schema and a bunch of

00:09:35.200 --> 00:09:40.240
other things. And because it's based on these open standards, then it can generate and it can also

00:09:40.240 --> 00:09:47.360
provide Swagger UI, as you were saying, which is this web user interface that shows all the information of

00:09:47.360 --> 00:09:53.440
the API. All the endpoints, where are the data, the shapes that you can send, and you can actually interact

00:09:53.440 --> 00:10:01.040
with the API directly from the routes without having to go to some documentation site and then update and

00:10:01.040 --> 00:10:08.000
the wiki gets updated and things like that. Yeah, absolutely. I built a weather service API for one of

00:10:08.000 --> 00:10:13.920
my courses. This has limited data. People don't try to use this for actual weather service. But it's a

00:10:13.920 --> 00:10:19.680
FastAPI API. And in addition to all the other cool things it does, like quickly generate the stuff it needs

00:10:19.680 --> 00:10:27.840
to, you can just go to slash docs and it'll give you the schemas. It'll give you API endpoints, the values

00:10:27.840 --> 00:10:34.320
that go in, the return value, all of this. I guess the most important aspect of this is probably Pydantic,

00:10:34.320 --> 00:10:38.480
correct? Yes, absolutely. Like the most important part. You define your stuff in Pydantic models and

00:10:38.480 --> 00:10:44.000
then that drives so many of these things. Yeah. So FastAPI is built on top of two tools. Pydantic does

00:10:44.000 --> 00:10:49.280
all the data stuff, data validation, serialization, documentation, and Starlette does all the web stuff.

00:10:49.280 --> 00:10:54.320
FastAPI just puts them together in a way that they work together. And that's some extra things on top.

00:10:54.320 --> 00:10:58.800
But Pydantic is the thing that powers all this data validation and all this automatic documentation.

00:10:58.800 --> 00:11:03.840
Right. Pydantic is so based on the same type annotations, the standard Python type annotations. So you

00:11:03.840 --> 00:11:08.960
can just like use the same intuition that you will have for a standard Python and they get all these

00:11:08.960 --> 00:11:17.280
data processing. Yeah. And FastAPI does several things with the Pydantic models. It does model

00:11:17.280 --> 00:11:22.800
binding, I guess I'll call it. That term's not super common in the Python world, but you can just say

00:11:22.800 --> 00:11:31.440
my API function or web function takes this model and then FastAPI will like create the Pydantic model and

00:11:31.440 --> 00:11:36.320
set the values and do the validation. And then also the return value, you can say it will drive this

00:11:36.320 --> 00:11:42.480
documentation and so on. So the reason I wanted to set the stage so much around Pydantic is that's one of the core

00:11:42.480 --> 00:11:50.560
elements of SQL model, right? And not just using that library, but so that it can be used as the models in

00:11:50.560 --> 00:11:57.760
FastAPI, right? Yes, exactly. So SQL model is a library, what they usually call an ORM. And if you don't know

00:11:57.760 --> 00:12:04.720
what an ORM is, it's just a library to connect SQL database with Python objects and classes. I don't

00:12:04.720 --> 00:12:09.600
know why we use the term ORM. I feel it's a bit abstract, but it's just a library to connect SQL

00:12:09.600 --> 00:12:17.760
databases with Python objects and classes. And the thing with SQL model is that it does a lot of work

00:12:17.760 --> 00:12:24.800
inside so that each model that you create is already a Pydantic model. It's not that it internally uses a

00:12:24.800 --> 00:12:30.960
Pydantic model or internally creates some additional Pydantic model is that each model is itself a

00:12:30.960 --> 00:12:37.440
Pydantic model. And at the same time, SQL models is built on top of Pydantic for data processing,

00:12:37.440 --> 00:12:42.560
validation, all this stuff. And another library that does all the work to communicate with SQL databases,

00:12:42.560 --> 00:12:47.680
which is called SQLAlchemy. And each one of these models is both Pydantic and SQLAlchemy.

00:12:47.680 --> 00:12:55.360
Yeah. It's an interesting marriage between Pydantic and SQLAlchemy. Much of the way that you work

00:12:55.360 --> 00:12:59.680
with it would be very familiar to people who do SQLAlchemy today, right?

00:12:59.680 --> 00:13:04.720
Yes. That's the idea that it will be very familiar for people that is already working with Pydantic,

00:13:04.720 --> 00:13:08.560
probably because they are using Plastic BI. But at the same time, it will be very familiar for people

00:13:08.560 --> 00:13:14.560
working with SQL model because it's just the same look and feel. And it's indeed a strange marriage

00:13:14.560 --> 00:13:20.160
because these two libraries are so different that getting them to connect and work together in the

00:13:20.160 --> 00:13:25.360
in the very different ways they are built. It was very, very strange. But they actually ended up like

00:13:25.360 --> 00:13:30.480
working quite the way. Yeah. I imagine that it was pretty tricky. You know, anytime that you get in

00:13:30.480 --> 00:13:37.440
the middle of an ORM and its model, I've tried to do that with other frameworks and said, oh, it would

00:13:37.440 --> 00:13:43.280
be great if I could say use inheritance in this way on my model so that there's not duplication. Like,

00:13:43.280 --> 00:13:50.160
oh, no, no, no, you can't do that because the thing really depends upon the exact class that

00:13:50.160 --> 00:13:57.760
derives from its sort of like ORM base class. That's what it uses for its determining what columns are

00:13:57.760 --> 00:14:03.120
there and so on. Right? Yeah. I mean, it was so crazy. I spent so much time in the

00:14:03.120 --> 00:14:08.480
the book that we were trying to figure out what was happening underneath and studying so much about

00:14:08.480 --> 00:14:14.320
like the black magic in Python, the stuff that I always fear, like all the meta classes and stuff

00:14:14.320 --> 00:14:19.040
and all that weird stuff. I studied so much of that to be able to mix these things together. But,

00:14:19.040 --> 00:14:25.040
yeah, like because they do things in a very different way. At the same time, that's facilitated,

00:14:25.040 --> 00:14:33.040
allowing one thing to do its job and the other thing to do its own job in their own particular ways. So,

00:14:33.040 --> 00:14:40.720
yeah, it was fun. Yeah. Very cool. Whenever I think about an ORM, the thing that I first go to focus on is

00:14:40.720 --> 00:14:49.200
the Python classes. Because for me, the whole point of the ORM is to let me talk to my database

00:14:49.200 --> 00:14:54.320
through those classes and model my application through those classes. Right? So let's maybe get

00:14:54.320 --> 00:15:04.000
started by talking about how do I create a class, a model, a SQL model model here that is both a

00:15:04.000 --> 00:15:09.840
Pydantic model and a SQLAlchemy-like model. I'm going to talk us through what does it look like?

00:15:09.840 --> 00:15:17.440
Cool. From SQL model, you will import this class, SQL model. And SQL model, you inherit from this class.

00:15:17.440 --> 00:15:23.440
You can, for example, create a class hero. And then let's jump to the internal parts of that. Then you

00:15:23.440 --> 00:15:28.240
will define some attributes for this class hero. For example, you could say that it has an ID,

00:15:28.240 --> 00:15:34.160
that this ID will be an integer. The way you declare that is with standard Python type annotations.

00:15:34.160 --> 00:15:38.880
You could say that it has a name and it's a string. If you are familiar with Pydantic, it basically

00:15:38.880 --> 00:15:41.280
could exactly be a Pydantic model. Yeah.

00:15:41.280 --> 00:15:42.800
The way you declare your ORM. Yeah.

00:15:42.800 --> 00:15:45.760
In the simple case, right? Yeah. Yeah, exactly.

00:15:45.760 --> 00:15:50.720
If it really is just an integer and it just has a number, you don't have to make it auto-increment or

00:15:50.720 --> 00:15:54.320
any weird stuff like that, right? Exactly. In the simplest cases, it will be,

00:15:54.320 --> 00:16:01.440
it will look just exactly like a Pydantic model. And in fact, it will be a Pydantic model. And then for

00:16:01.440 --> 00:16:06.320
some particular cases where you need to add a little bit of extra information to tell SQL

00:16:06.320 --> 00:16:11.840
model and SQLAlchemy underneath to tell it, "Hey, this does this thing with the database," then you can

00:16:11.840 --> 00:16:15.840
pass additional parameters and additional configurations. So for example, when you create

00:16:15.840 --> 00:16:23.280
the ID of this class, this will be the ID of the table and it has to be a primary key. So then you can

00:16:23.280 --> 00:16:30.240
use the function field to say, "Hey, this still has a default value of none, but I need this particular

00:16:30.240 --> 00:16:35.680
field or this particular attribute or this particular column, however you want to call it. I need this to

00:16:35.680 --> 00:16:43.840
be the primary key." And then that information is passed through to SQLAlchemy underneath, which is the one that does all the work.

00:16:43.840 --> 00:16:49.840
And there's something particularly interesting here is that you are saying, "Hey, this has a default value of none,

00:16:49.840 --> 00:16:58.320
and that none default value will be used by Pydantic in the Pydantic side of things, but at the same time, it will be

00:16:58.320 --> 00:17:04.880
used in the SQL side of things. So in the database, this will have also the particular default value.

00:17:04.880 --> 00:17:09.360
In the case of the primary key, it's just because when you create a model, you still don't know what

00:17:09.360 --> 00:17:15.200
the primary key is until you say." Most of the time, the database generates that. You could say

00:17:15.200 --> 00:17:21.280
your primary key could be your email address, but it's common to have it just auto-generated by the

00:17:21.280 --> 00:17:26.080
database. Yeah. A UUID or auto-increment integer or something like that. Exactly. Yeah.

00:17:26.080 --> 00:17:32.240
For those cases, you want to have the type annotations very precise so that your code can tell you,

00:17:32.240 --> 00:17:37.280
"Hey, this could be known at some point." That's just a particular detail. But the thing is,

00:17:37.280 --> 00:17:42.800
the important thing is that you use standard type annotations to declare attributes, and then these

00:17:42.800 --> 00:17:48.800
will be mapped to the data model in Pydantic, but at the same time will be mapped to the table in the SQL

00:17:48.800 --> 00:17:58.720
data. Nice. So it kind of behaves in the two ways. And that means that what you put into your database is pretty much what your API model is as well, right? Exactly.

00:17:58.720 --> 00:18:06.720
This portion of Talk Python To Me is brought to you by Datadog. Are you having trouble visualizing latency

00:18:06.720 --> 00:18:11.840
and CPU or memory bottlenecks in your app? Not sure where the issue is coming from or how to solve it?

00:18:11.840 --> 00:18:18.000
Datadog seamlessly correlates logs and traces at the level of individual requests, allowing you to quickly

00:18:18.000 --> 00:18:22.800
troubleshoot your Python application. Plus, their continuous profiler allows you to find the most

00:18:22.800 --> 00:18:28.400
resource consuming parts of your production code all the time at any scale with minimal overhead.

00:18:28.400 --> 00:18:33.360
Be the hero that got that app back on track at your company. Get started today with a free trial at

00:18:33.360 --> 00:18:40.960
talkpython.fm/datadog or just click the link in your podcast player's show notes. Get the insight you've been missing with Datadog.

00:18:42.880 --> 00:18:56.880
That's the idea. Like in the most basic situation, the cool thing is that with this approach and with this tool, you can then create additional models that don't map to one particular table in the database,

00:18:56.880 --> 00:19:08.880
but are just for handling data with the API. For example, if you create, if you have an API that receives data to create a user, it will probably receive a password from the user. It will have the username and the password.

00:19:08.880 --> 00:19:18.480
And you want to be able to have that information in the model that you want to receive in the API. But you don't want to save a password as plain text in the database.

00:19:18.480 --> 00:19:34.880
You don't? Isn't that the easiest way? I get these warnings from these various sites like, oh, your password can't be more than eight characters long because no, yeah, please don't save it in the database. That's a really interesting scenario, right? You need to receive it on one end, but you must not put it into the database.

00:19:34.880 --> 00:19:35.880
Exactly.

00:19:35.880 --> 00:19:37.080
You must, it should not carry on.

00:19:37.080 --> 00:19:51.080
And for example, and then in that same situation, you create the user and you want to return the information of the user back to whatever is the client. You don't want to return the plain text password. You want to say, hey, this is the username, but like, that's it.

00:19:51.080 --> 00:19:56.680
Yeah. Yeah. Probably not. It's very unlikely that you want to return the hash as well. You just, you don't want it to return at all. Right?

00:19:56.680 --> 00:19:57.680
Yeah. How do you handle that?

00:19:57.680 --> 00:20:05.680
Yeah. For these particular cases is where SQL model will shine because you can create one base model that will have like all the base attribute scores.

00:20:05.680 --> 00:20:10.680
I'm probably will have the name, the last name, the address, the email, blah, blah, blah.

00:20:10.680 --> 00:20:38.680
And then you can inherit from that model and they have different models for the particular use cases. For example, for creating data. So you will have a plain text password and for returning data, you will have no password at all. But then one of these models will be the actual model that stays in the database. The one that reflects the information in the database. And this one is the one that will have the hash password. But you didn't have to duplicate all the information for the model because they only inherit from the same base model.

00:20:38.680 --> 00:20:42.680
Is that the section that I got on the screen here that says multiple models with FastAPI?

00:20:42.680 --> 00:20:43.680
Yes.

00:20:43.680 --> 00:20:44.680
Like how you do that?

00:20:44.680 --> 00:20:45.680
Yes, exactly.

00:20:45.680 --> 00:21:04.680
Yeah. So the idea is obviously you have got some shared information about the user, like the email and their name and stuff. You want to share that probably their ID, but you don't want to, you don't want to share, say there, like you said, the password or whether or not they're an admin on the site or those kinds of things you probably don't want to exchange over the API. Right?

00:21:04.680 --> 00:21:05.680
Exactly.

00:21:05.680 --> 00:21:31.400
Exactly. And if you need to duplicate all the information for each one of these particular models, there's a high chance that at some point, whenever you refactor the code, some part will be out of sync. And then you will have a bunch of errors and a bunch of bugs that are very difficult to detect. When you have duplication of code and you have to synchronize it by hand, it makes, it creates a lot of potential bugs that are very difficult to detect.

00:21:31.400 --> 00:21:46.140
Yeah. So in the way you do your models, this is pretty neat. One of the things that you do is you've got your model hierarchy, you've got SQL model, which is the base class of all the things that interact with, well, SQL model.

00:21:46.140 --> 00:22:00.600
And those are typically the classes that you create that would be like SQLAlchemy or Django ORM models. But in your world, you can have inheritance. And then somewhere in that hierarchy, you set table equals true as you create the class.

00:22:00.600 --> 00:22:15.540
So it's not necessarily that just, oh, you derive from this class. So that's a table. It gives you more flexibility and go, this part is a table. That part is a table. Like in the scenario we're talking about, you have a base user where there's a name and a password, a hash password and stuff.

00:22:15.540 --> 00:22:21.540
No, sorry. You wouldn't want to put that. You would put like your shared stuff into the base class.

00:22:21.740 --> 00:22:22.620
And then you, right.

00:22:22.620 --> 00:22:24.940
Name, last name, address, email, I don't know.

00:22:24.940 --> 00:22:31.580
And then the thing that derives from a user would derive from like user base, which would say like table equals true and it could have its secrets there.

00:22:31.580 --> 00:22:32.760
Exactly. Exactly.

00:22:32.760 --> 00:22:33.360
Okay.

00:22:33.360 --> 00:22:46.520
So that makes a lot of sense. Inbound. What about outbound? So I've got a FastAPI endpoint, right? It could even be Flask or whatever, right? And I've done a query to the database and I get the table version that has the secrets.

00:22:46.740 --> 00:23:03.400
I can easily go to FastAPI and say the response model is the base thing. So the documentation is right. But if I go to the object I got from the database and I say as dictionary or to dictionary, I forgot exactly what the right term is, but the thing that sends it back, it's going to include everything in it, isn't it?

00:23:03.400 --> 00:23:19.460
So this is one of those particular details of FastAPI that I think people in many cases miss. And is that in FastAPI, you can say, hey, this is the response model. So this is the model that I want you to use for the data that I'm sending back.

00:23:19.460 --> 00:23:34.680
So this is the most obvious result of that is that in the automatic documentation, you will get the schema of what is the response data. And that is like the most obvious and visible. But FastAPI will also use that same model to filter out the data.

00:23:34.680 --> 00:23:48.660
So if you say, if you say the response model is user out, for example, and the class user out, which is a Pydantic class or something like that, this class user out doesn't include the hash password.

00:23:48.800 --> 00:24:05.540
From the function, you can return an object that includes the hash password or a dictionary that includes the hash password. But FastAPI will omit that field. And FastAPI will only return the particular fields that were defined in the response model that you say that will be returned.

00:24:05.540 --> 00:24:12.740
Okay. I did not know that that also affected the outbound data, not just the documentation. That's pretty interesting.

00:24:13.020 --> 00:24:23.140
Yeah. And in fact, that's in many cases, people ask, why does FastAPI use this parameter response model instead of using the return type annotation?

00:24:23.140 --> 00:24:31.740
Because in Python, when you create a function, you can define what are the types of the parameters that the function receives. And you can also define what is the return of that particular function.

00:24:32.100 --> 00:24:43.140
If FastAPI use the return value, but then you can say like, hey, the return value is this user out, but then the object that you were returning from that particular function was different object.

00:24:43.140 --> 00:24:54.620
Then the editor will complain, the tooling and the tools that detect those typing errors like mypy will complain and they will detect, hey, you're saying that you're returning something, but you're returning a completely different thing.

00:24:54.620 --> 00:24:59.740
So that's the reason why their return type is not what is used to extract that information.

00:24:59.740 --> 00:25:05.200
And instead it uses this particular configuration responsible because it's used for filtering data.

00:25:05.200 --> 00:25:06.420
Right. Okay. Interesting.

00:25:06.420 --> 00:25:15.860
So for people who don't know, haven't seen this in action, you put a decorator like an app dot or API dot get, for example, just like you would say in Flask or something.

00:25:15.860 --> 00:25:21.980
And you say, here's the URL, but then you also may put response model equals some Pydantic type in FastAPI.

00:25:21.980 --> 00:25:24.240
And that drives a Swagger documentation.

00:25:24.240 --> 00:25:30.240
And I am learning now drives the filtering of the allowed return values as well, which is pretty excellent.

00:25:30.240 --> 00:25:33.240
Yeah. In fact, it will also validate that.

00:25:33.240 --> 00:25:38.900
So if you are saying, hey, this will return this data and then whatever you're returning doesn't include that.

00:25:39.260 --> 00:25:47.620
That will actually be an error on the server because you are saying that the contract is that I will return this data, but then suddenly you are not returning it.

00:25:47.620 --> 00:25:52.840
Then it will raise an error inside of the server and it will tell you, hey, the data that you're sending is incorrect.

00:25:52.840 --> 00:25:54.540
So there's something going on here.

00:25:54.540 --> 00:26:00.200
There's something wrong with your code because you're sending something invalid from what you say that you were going to send.

00:26:00.200 --> 00:26:01.240
That's pretty fantastic.

00:26:01.240 --> 00:26:05.220
Okay. I didn't realize it made such great use of that response model.

00:26:05.220 --> 00:26:09.960
So that's just a whole nother level to bringing Pydantic into that world.

00:26:09.960 --> 00:26:13.820
So there's a bunch of comments and thoughts here in the audience.

00:26:13.820 --> 00:26:16.380
So I kind of want to bring some of them in because there's a bunch of great ones.

00:26:16.380 --> 00:26:22.740
First of all, former guest Waylon says your mustache is fabulous, which is always required when you're on a video.

00:26:22.740 --> 00:26:24.100
Thank you very much.

00:26:24.100 --> 00:26:27.360
Sveta Link says big like on FastAPI.

00:26:27.540 --> 00:26:30.340
I think FastAPI is absolutely a good thing.

00:26:30.340 --> 00:26:30.760
Hey, thank you.

00:26:30.760 --> 00:26:33.000
And Jacqueline also very much.

00:26:33.000 --> 00:26:35.820
Poplin says, I have a question.

00:26:35.820 --> 00:26:41.780
If the data schema is complex and has nested JSON structure, in what case would you validate?

00:26:41.780 --> 00:26:47.060
Like it's pretty straightforward to just nest the Pydantic, but this brings us, you know,

00:26:47.060 --> 00:26:52.140
if you're going to be in a world where you're nesting Pydantic things, you may want to save them to the database.

00:26:52.140 --> 00:26:54.280
What's the story on relationships?

00:26:55.180 --> 00:27:00.020
And this basically, I've received some data that is like nested related data.

00:27:00.020 --> 00:27:01.760
What do I do in SQL model?

00:27:01.760 --> 00:27:07.540
So if you need to receive some complex data structure and you need to extract the information,

00:27:07.540 --> 00:27:13.380
you can declare, you can declare models with Pydantic or with SQLAlchemy, sorry, with SQL model,

00:27:13.380 --> 00:27:15.300
say that, hey, this is just a data model.

00:27:15.300 --> 00:27:21.820
And then you can manually extract the sub components and then just add them to the database independently or something like that.

00:27:21.820 --> 00:27:31.400
There wouldn't be a straightforward way to say like, hey, I received this giant JSON and automatically generate a bunch of different models that don't exist yet or something like that.

00:27:31.400 --> 00:27:34.840
Or to automatically infer where to put each information.

00:27:34.840 --> 00:27:36.480
It will be as straightforward.

00:27:36.480 --> 00:27:40.560
Like it will have a lot of different design possibilities.

00:27:40.780 --> 00:27:42.880
So it will be easy to get it drawn.

00:27:42.880 --> 00:27:48.020
So the way that you will do it is that you define the complex data shape that you want to receive.

00:27:48.020 --> 00:27:59.140
And then once you take it, you just extract it, each part of the information and each particular object or each particular data point that you want to then say to the database.

00:27:59.140 --> 00:28:08.780
Now, to return data to the user, with SQL model, you can have relationships and relationships between different tables,

00:28:08.780 --> 00:28:10.600
have like automatic joins and all that stuff.

00:28:10.600 --> 00:28:14.720
This is all thanks again to SQLAlchemy, which is the thing that works underneath.

00:28:14.720 --> 00:28:16.000
It already models that, yeah.

00:28:16.000 --> 00:28:16.740
Yeah, exactly.

00:28:16.740 --> 00:28:20.920
But then you can use that information and you can just like declare the models.

00:28:20.920 --> 00:28:31.560
And this again works well with this idea of having narratives to be able to declare, hey, I want to return this model and I want it to include this particular relationship model.

00:28:31.560 --> 00:28:34.180
So it will include the information from other tables.

00:28:34.180 --> 00:28:37.060
We will just extract that information and return it to the client.

00:28:37.060 --> 00:28:37.800
That's really cool.

00:28:37.800 --> 00:28:40.440
What about lazy loading?

00:28:40.440 --> 00:28:42.640
I'll ask this in two aspects.

00:28:42.640 --> 00:28:52.120
If I've got a relationship, I can do a join or a subquery load on it in SQLAlchemy so that if I know I'm going to be traversing that relationship,

00:28:52.120 --> 00:28:58.560
I don't end up with the dreaded N plus one performance problem where I thought I was doing one query and I'm doing 51 queries.

00:28:58.560 --> 00:29:02.580
If I got a, you know, 51 or 50 results back, something like that.

00:29:02.780 --> 00:29:05.920
Is that support flow through SQL model as well?

00:29:05.920 --> 00:29:06.700
The joins?

00:29:06.700 --> 00:29:06.940
Yeah.

00:29:06.940 --> 00:29:15.060
So the thing is that SQL load actually just like exposes the same interface as SQLAlchemy because it's actually just using SQLAlchemy underneath.

00:29:15.060 --> 00:29:19.400
And SQLAlchemy supports everything, including like lazy loading.

00:29:19.400 --> 00:29:23.320
SQLAlchemy actually supports things that are not supported by many other ORMs.

00:29:23.540 --> 00:29:24.600
I forgot the name.

00:29:24.600 --> 00:29:28.160
Having primary keys that are composed of different, of several columns.

00:29:28.160 --> 00:29:29.060
Things like that.

00:29:29.060 --> 00:29:31.340
Composite indexes and composite keys.

00:29:31.340 --> 00:29:32.280
Composite keys.

00:29:32.280 --> 00:29:32.460
Yeah.

00:29:32.460 --> 00:29:35.860
There's a bunch of things that SQLAlchemy supports.

00:29:35.860 --> 00:29:43.900
If SQLAlchemy supports them, then the SQL model automatically supports them because SQL model is just generated directly from SQLAlchemy.

00:29:43.900 --> 00:29:44.820
Yeah, that's really cool.

00:29:44.820 --> 00:29:59.600
Now, the question I was thinking about is if I have a result from the database that has relationships, a relationship, and I return it from a FastAPI endpoint, is that going to go and start iterating the relationship?

00:29:59.600 --> 00:30:08.780
Do I need to be worried about N plus one problems by returning these models that then are getting serialized in eager ways?

00:30:08.780 --> 00:30:09.320
Yeah.

00:30:09.460 --> 00:30:14.060
You know, where it's like tracing through all the relationships so it can build the whole JSON to get it back out.

00:30:14.060 --> 00:30:16.740
And then return the whole database in a single JSON.

00:30:16.740 --> 00:30:18.240
Wow, that took a while.

00:30:18.240 --> 00:30:18.820
That was slow.

00:30:18.820 --> 00:30:19.560
That would be so painful.

00:30:19.560 --> 00:30:20.260
Yeah.

00:30:20.260 --> 00:30:30.700
No, by default, FastAPI and SQL model won't include relationships in models, won't include them in the data that is returned back.

00:30:30.900 --> 00:30:41.580
But if you need to include some of those, you can declare, again, using inheritance, you can declare a different model that defines, hey, this relationship, this particular attribute, this should be included.

00:30:41.580 --> 00:30:48.400
That way you can define that particular one in the specific endpoints that you want to include the information in the resulting value.

00:30:48.400 --> 00:30:49.120
Right, okay.

00:30:49.120 --> 00:31:00.900
That will work to force SQL model and to force, well, FastAPI and SQL model to do all the N plus one queries and to just extract information and send it back.

00:31:00.900 --> 00:31:12.340
But if you are returning that data including the relationships, you will probably want to eagerly load that information, which is something that is supported by SQL model and by SQL.

00:31:12.340 --> 00:31:19.300
So you will load the information that you need including the relationships and then you just return that object directly and you define the model.

00:31:19.300 --> 00:31:21.500
Hey, I want this to include the relationships.

00:31:21.500 --> 00:31:23.860
So it will just like include the information that is already there.

00:31:23.860 --> 00:31:31.100
I can see that this N plus one issue is without the join or eagerly load is happening through a profiler.

00:31:31.100 --> 00:31:41.500
If I was doing this in something like Django or Pyramid, I could look into the debug toolbar and it'll actually show me the SQLAlchemy statements that are running.

00:31:41.500 --> 00:31:44.160
It'll be like, why are there 50 queries on this page?

00:31:44.600 --> 00:31:52.600
It's harder, I suspect, in FastAPI, especially if it's operating in API mode where it doesn't have like debug toolbars and stuff like that.

00:31:52.600 --> 00:31:56.680
Probably one way you could see it is to say echo equals true on the engine.

00:31:56.680 --> 00:31:57.600
Yeah, exactly.

00:31:57.600 --> 00:31:58.020
Exactly.

00:31:58.020 --> 00:32:09.020
Because FastAPI is not integrated with any database and SQL just makes it super easy to work with FastAPI, but SQL could be used with any other framework.

00:32:09.020 --> 00:32:09.960
That was the intention.

00:32:09.960 --> 00:32:13.320
SQL model doesn't depend on FastAPI and FastAPI doesn't depend on SQL.

00:32:13.540 --> 00:32:14.780
So you just integrate very well.

00:32:14.780 --> 00:32:24.060
But then you could just enable the table thing with SQLAlchemy that will then show all the particular SQL statements and show you, hey, this is what is running.

00:32:24.060 --> 00:32:24.760
This is what is happening.

00:32:24.760 --> 00:32:25.040
Right.

00:32:25.040 --> 00:32:25.260
Yeah.

00:32:25.260 --> 00:32:32.540
So if you're connecting to the database, then this is a SQLAlchemy thing, but obviously it'll flow through.

00:32:32.540 --> 00:32:32.980
Right.

00:32:32.980 --> 00:32:37.360
You just say when you create the engine, you give it the connection string, you can say echo equals true.

00:32:37.620 --> 00:32:48.140
And if you are doing queries that are doing a bunch of indirect behind the scene, lazy queries for you, your console window, your terminal, whatever is just going to blow up with a query.

00:32:48.140 --> 00:32:51.040
You're like, why is so much SQL screaming by, right?

00:32:51.520 --> 00:32:51.960
Yeah, exactly.

00:32:51.960 --> 00:32:52.080
Exactly.

00:32:52.080 --> 00:32:52.780
Exactly.

00:32:52.780 --> 00:32:53.900
That's what it will work.

00:32:53.900 --> 00:32:55.680
Let's talk about editor support really quick.

00:32:55.680 --> 00:33:13.440
So one of the things that's really nice about Pydantic is it requires you to state the types, whether those are fundamental types, whether those are nullable types, like optional of int, or they're nested types, like a user contains an address Pydantic model.

00:33:13.700 --> 00:33:17.700
All of those scenarios result in really good editor support, right?

00:33:17.700 --> 00:33:18.180
Yeah.

00:33:18.180 --> 00:33:19.000
Yeah, exactly.

00:33:19.000 --> 00:33:21.660
And what's the story for editors in SQL model?

00:33:21.660 --> 00:33:24.900
That's something you specifically call out about how it has good support there.

00:33:24.900 --> 00:33:32.800
So if you check the source code for SQL model, it's actually super short, but it's just like a bunch of a lot of tricks together.

00:33:32.980 --> 00:33:46.020
And many of those tricks are actually about type annotations because the thing that allows your editor to provide the auto-completion and the inline errors are the declarations of these types, the type annotations or type hits.

00:33:46.580 --> 00:33:56.900
And SQL model does a lot of internal work so that whenever you use any part of SQL model, you will get that type information in your editor.

00:33:56.900 --> 00:34:16.040
For example, if you query some model, query some table to get information, to get data from the database, the result that you get back, the object that you get back, will have internally all that type information so that the editor will be able to provide you with all the autocoption and all the inline errors and all those things.

00:34:16.460 --> 00:34:33.420
SQL model, in fact, sacrifices some of the more advanced or obscure or sophisticated use cases that SQLAlchemy supports and sacrifices those to instead get very good autocompletion and inline errors everywhere in the code.

00:34:33.420 --> 00:34:43.580
And this is another thing that the results and clear in SQL model is that it uses some draft standards that are not even implemented yet.

00:34:43.780 --> 00:34:51.520
I don't know, I don't know, I'm not part of the standards, official standards yet, but they are already supported by some editors.

00:34:51.520 --> 00:35:00.640
For example, Visual Studio Code already supports providing autocompletion when you are creating a new instance of a particular class or for the particular class of a model.

00:35:00.960 --> 00:35:11.460
Having this autocompletion is not very easy to do with other tools because the editor doesn't have any information about what are the parameters that you can pass, what are the arguments that you can pass?

00:35:11.460 --> 00:35:12.000
Right.

00:35:12.000 --> 00:35:23.880
When you create a Pydantic model, it doesn't in anywhere indicate here is the constructor or initializer and here are the keyword arguments that happen to be all the static values.

00:35:24.700 --> 00:35:29.180
It will just say like keyword arguments or like data, star, star, something like that.

00:35:29.180 --> 00:35:30.160
Yeah.

00:35:30.160 --> 00:35:32.980
But I always think thanks for nothing when I see that.

00:35:32.980 --> 00:35:34.160
Yeah, exactly.

00:35:34.520 --> 00:35:38.780
But actually Pydantic 1.9 includes this same trick.

00:35:38.780 --> 00:35:41.980
So now you get autocompletion in Visual Studio Code.

00:35:41.980 --> 00:35:49.880
In PyChart, you already have autocompletion with Pydantic because they have a plugin for Pydantic to provide autocompletion for those things.

00:35:49.880 --> 00:35:51.520
But it requires this particular plugin.

00:35:51.520 --> 00:35:55.560
Now with this extension, you can get also autocompletion in VS Code.

00:35:55.560 --> 00:36:01.360
And with the same extension without needing any plugin, you get autocompletion for SQL model in Visual Studio Code.

00:36:01.360 --> 00:36:16.640
I think the people from PyChart were also taking out to maybe support the same standard, which will allow PyChart to provide automatic autocompletion for SQL model and other libraries like Pydantic and Attrs and others.

00:36:16.860 --> 00:36:17.900
Yeah, that's great.

00:36:17.900 --> 00:36:27.400
Definitely the widespread use of Pydantic effectively is forcing everyone to go like, all right, how can we make this work better on the creation side?

00:36:27.400 --> 00:36:33.680
So RJL out there has a comment, which then leads me to an interesting question.

00:36:33.680 --> 00:36:34.640
Says, I'm old fashioned.

00:36:34.640 --> 00:36:36.320
I use direct SQL statements.

00:36:36.320 --> 00:36:37.040
No ORM.

00:36:37.040 --> 00:36:38.900
I really need to take the time to go down this route.

00:36:38.900 --> 00:36:40.900
Indeed, I do think so.

00:36:40.900 --> 00:36:42.920
It's certainly worthwhile.

00:36:42.920 --> 00:36:45.900
What are your thoughts on using straight SQL versus not?

00:36:45.900 --> 00:36:46.840
Then I'll ask my question.

00:36:46.840 --> 00:36:52.220
So I think, you know, like it just, it's a lot about taste and how people prefer to code.

00:36:52.220 --> 00:37:02.080
There's a lot of people that are so comfortable with SQL and that can do so many things with SQL very easily that it's just more efficient to just use SQL directly.

00:37:02.080 --> 00:37:10.080
For me, some of the advantages with ORMs is that I get inline errors that I get autocompletion for what is the name of the attribute.

00:37:10.080 --> 00:37:15.880
If I forget that I say secret underscore name, the editor will autocomplete that for me.

00:37:15.880 --> 00:37:27.420
But if I'm typing that inside of just a long string in Python using SQL, then I have to remember because no one will tell me that I have a syntax error in my SQL or that I'm using an attribute that doesn't exist.

00:37:27.420 --> 00:37:29.880
One of the things that actually blew my mind is PyCharm.

00:37:30.700 --> 00:37:40.580
If you set it up to, if you basically connect the database to your project, it'll give you autocomplete and error checking inside strings inside Python.

00:37:40.580 --> 00:37:41.440
That's super cool.

00:37:41.440 --> 00:37:43.640
For your schema, which is amazing.

00:37:44.100 --> 00:37:49.860
That said, I never do that because to me, one of the things that is super valuable, one is this autocomplete.

00:37:49.860 --> 00:37:52.680
The other is refactoring as well.

00:37:52.680 --> 00:37:54.420
Like, oh, did we change the name of that?

00:37:54.420 --> 00:38:00.680
Well, oh, there was that one query we didn't update and now it crashes in production, but only sometimes when it hits this case.

00:38:01.460 --> 00:38:08.220
And, you know, it's just the way it sticks together and stays consistent to me seems a lot stronger if you're using models as well.

00:38:08.220 --> 00:38:08.360
Yeah, absolutely.

00:38:08.360 --> 00:38:10.480
And also the ability to swap backends, right?

00:38:10.560 --> 00:38:15.140
The way you do parameterized queries is different across different database backends.

00:38:15.140 --> 00:38:15.560
Yeah.

00:38:15.560 --> 00:38:28.760
Also, if you write SQL by hand, then you have to be super careful and probably you have to be a SQL wizard and, like, know how to sanitize all the data that you're putting or, like, you know, otherwise you could end up in nasty situations.

00:38:29.260 --> 00:38:36.200
But, like, I'm saying that there's a lot of people that prefer, really prefer writing SQL directly.

00:38:36.200 --> 00:38:42.800
You know, like, the same author of Pydantic, which SQL model is based on, prefers to write SQL directly.

00:38:42.800 --> 00:38:46.720
And, like, he's using FastAPI and everything, but he's just more comfortable to me.

00:38:46.720 --> 00:38:55.640
And the author of PyPy, PsychoPG, the driver for PostgreSQL, he just uses SQL directly.

00:38:55.640 --> 00:38:57.740
It's just, like, more comfortable for him.

00:38:57.740 --> 00:39:00.920
He uses FastAPI a lot, but still, like, it's just more comfortable.

00:39:00.920 --> 00:39:02.620
So I guess, like, it depends a lot.

00:39:02.620 --> 00:39:08.920
For me, I depend a lot on the tooling and editor support and refactoring, as you were saying.

00:39:08.920 --> 00:39:13.620
Like, if I change a name, I know that it's changed everywhere because I won't remember.

00:39:13.620 --> 00:39:14.500
I won't remember.

00:39:14.500 --> 00:39:15.980
Well, did I make this mistake?

00:39:15.980 --> 00:39:16.260
Yeah.

00:39:16.260 --> 00:39:17.300
Yeah, absolutely.

00:39:17.300 --> 00:39:19.860
Martin in the audience asked an interesting question.

00:39:19.860 --> 00:39:21.320
Down here is a better example.

00:39:21.320 --> 00:39:28.720
One of the challenges of ORMs is to make set-based operations apply back to the database.

00:39:28.720 --> 00:39:31.020
Like, I want to change this field.

00:39:31.020 --> 00:39:38.380
Like, I want to set a is on sale flag to true for all products where the price is less than $10.

00:39:38.380 --> 00:39:39.320
Right?

00:39:39.400 --> 00:39:40.600
Where I'm not going to pull.

00:39:40.600 --> 00:39:44.680
I don't want to go, let me query all products whose price are less than $10.

00:39:44.680 --> 00:39:47.260
Change it on the object and then push those changes.

00:39:47.260 --> 00:39:48.640
I want to push the...

00:39:48.640 --> 00:39:51.620
I just want to say, update where this set that.

00:39:51.620 --> 00:39:52.140
You know what I mean?

00:39:52.140 --> 00:39:52.740
Yeah, yeah.

00:39:52.880 --> 00:39:55.200
What's the story about that with SQL model?

00:39:55.200 --> 00:39:59.940
Because that's one of the things that can really just hammer productivity or speed, I guess,

00:39:59.940 --> 00:40:04.360
is if you've got to, like, pull back a whole bunch of stuff to just make sort of consistent

00:40:04.360 --> 00:40:05.300
changes across them.

00:40:05.300 --> 00:40:05.760
You know what I'm saying?

00:40:05.760 --> 00:40:06.380
Yeah, yeah.

00:40:06.380 --> 00:40:11.860
Like, this is one of the use cases where you will want to interact directly with SQLAlchemy.

00:40:11.860 --> 00:40:16.480
And you can do that through SQL model, but you can't write, like, where it's as complex

00:40:16.480 --> 00:40:21.140
as you want through SQL model, but, like, just using pure SQLAlchemy underneath.

00:40:21.140 --> 00:40:25.700
And, like, you can use, like, very advanced things with SQLAlchemy.

00:40:25.700 --> 00:40:32.480
SQL model focuses a lot on, like, the simplest and most common use cases, providing, like,

00:40:32.480 --> 00:40:34.300
the best developer experience.

00:40:34.300 --> 00:40:39.500
You know, like, certainty that the code is as ever free as possible because you have all

00:40:39.500 --> 00:40:41.080
these type annotations and all these type checks.

00:40:41.080 --> 00:40:46.120
But for any case that is a little bit more advanced, you can just, like, drop down directly

00:40:46.120 --> 00:40:47.180
to SQLAlchemy.

00:40:47.180 --> 00:40:51.120
And because SQL model is just pure SQLAlchemy, the models are themselves.

00:40:51.120 --> 00:40:52.400
Just SQLAlchemy.

00:40:52.400 --> 00:40:54.540
So you can just, like, use SQLAlchemy directly.

00:40:54.540 --> 00:41:00.680
In fact, you could use one of these models with a SQLAlchemy engine directly, and it would

00:41:00.680 --> 00:41:00.820
work.

00:41:00.820 --> 00:41:01.840
Okay.

00:41:01.840 --> 00:41:06.200
Sky points out in the audience that you were too humble to call out the PR that got that

00:41:06.200 --> 00:41:09.300
autocomplete for VS Code was actually by Tandic.

00:41:09.300 --> 00:41:10.020
It was by you.

00:41:10.020 --> 00:41:10.560
So well done.

00:41:10.560 --> 00:41:11.880
Thank you.

00:41:11.960 --> 00:41:13.260
Keep moving it forward on both fronts.

00:41:13.260 --> 00:41:15.500
So what about performance?

00:41:15.500 --> 00:41:22.040
There's extra goodness in the validation and the type conversions and stuff like that of,

00:41:22.040 --> 00:41:22.720
say, Pydantic.

00:41:22.720 --> 00:41:29.940
But is there a large overhead for using this, say, over SQLAlchemy over, say, raw SQL?

00:41:29.940 --> 00:41:35.860
Yeah, well, so when you use, when you declare a model and you say, hey, this is table model,

00:41:35.860 --> 00:41:40.980
so this is, like, the equivalent of a SQLAlchemy model, then a lot of the validation and that

00:41:40.980 --> 00:41:42.760
stuff with Pydantic is a very written.

00:41:42.760 --> 00:41:47.400
So when you create a model, it will not be validated on creation for that particular table

00:41:47.400 --> 00:41:50.920
because this will be handled directly by SQLAlchemy.

00:41:50.920 --> 00:41:56.220
And for example, with SQLAlchemy, you can create an instance of a model without setting

00:41:56.220 --> 00:41:59.520
all the attributes and then you can set the attributes manually afterwards.

00:41:59.520 --> 00:42:04.000
If Pydantic was doing validation for that, like, that will explode and that will say, hey,

00:42:04.000 --> 00:42:04.560
this is invalid.

00:42:04.560 --> 00:42:04.760
Yeah.

00:42:04.760 --> 00:42:09.880
So when you are working with SQLAlchemy alone, well, like, with the SQL parts alone through

00:42:09.880 --> 00:42:11.900
SQL model, then it's just, like, using SQLAlchemy.

00:42:11.900 --> 00:42:17.560
Okay, so it's not really any different in terms of, like, whatever SQLAlchemy does, this

00:42:17.560 --> 00:42:18.440
does in terms of performance.

00:42:18.440 --> 00:42:19.120
Exactly, exactly.

00:42:19.120 --> 00:42:24.480
It just has, like, and actually, the code is so, so sleek, it's so little that, like, whatever

00:42:24.480 --> 00:42:27.460
is the overhead will be, I will think it will be negligible.

00:42:27.460 --> 00:42:33.580
At the same time, I'm not optimizing for, like, squeezing the maximum performance, but

00:42:33.580 --> 00:42:38.600
for getting the maximum correctness in the code and the best developer experience.

00:42:38.600 --> 00:42:45.040
Because I feel it helps a lot more to be a lot, as a developer, building the tool and making

00:42:45.040 --> 00:42:49.600
sure that it's all correct, than having the code super fast, but very difficult to debug and

00:42:49.600 --> 00:42:51.460
to understand and to write correctly.

00:42:51.660 --> 00:42:56.120
Okay, so I guess you just got to decide, like, is an ORM the right fit at all?

00:42:56.120 --> 00:42:56.420
Yeah.

00:42:56.420 --> 00:42:59.300
And if it is, then this is a pretty good choice if you like this API.

00:42:59.300 --> 00:43:00.220
Yeah, absolutely.

00:43:00.220 --> 00:43:05.740
If you were needing, like, the maximum performance that you can get, you will probably end up

00:43:05.740 --> 00:43:10.180
just, like, getting an async, an async drive it directly and just, like, writing SQL directly

00:43:10.180 --> 00:43:14.060
for the particular endpoint that needs this extra boosting performance.

00:43:14.420 --> 00:43:18.760
But for most of the other cases, this will probably help making sure that the code is

00:43:18.760 --> 00:43:23.180
correct and making sure that you can write quickly and, like, be done with the feature

00:43:23.180 --> 00:43:24.060
that you're implementing quickly.

00:43:26.060 --> 00:43:29.380
This portion of Talk By The Nome is brought to you by Tonic.ai.

00:43:29.380 --> 00:43:35.180
Creating quality test data for developers is a complex, never-ending chore that eats in the

00:43:35.180 --> 00:43:36.700
valuable engineering resources.

00:43:36.700 --> 00:43:38.900
Random data doesn't do it.

00:43:38.900 --> 00:43:42.660
And production data is not safe or legal for developers to use.

00:43:42.660 --> 00:43:47.820
What if you could mimic your entire production database to create a realistic data set with

00:43:47.820 --> 00:43:49.440
zero sensitive data?

00:43:50.200 --> 00:43:52.480
Tonic.ai does exactly that.

00:43:52.480 --> 00:43:58.100
With Tonic, you can generate fake data that looks, acts, and behaves like production data

00:43:58.100 --> 00:44:00.420
because it's made from production data.

00:44:00.420 --> 00:44:06.140
Using their universal data connectors and a flexible API, Tonic integrates seamlessly into

00:44:06.140 --> 00:44:11.780
your existing pipelines and allows you to shape and size your data to scale, realism, and degree

00:44:11.780 --> 00:44:13.140
of privacy that you need.

00:44:13.140 --> 00:44:20.080
Their platform offers advanced subsetting, secure de-identification, and ML-driven data synthesis

00:44:20.080 --> 00:44:24.500
to create targeted test data for all your pre-production environments.

00:44:24.500 --> 00:44:30.720
Your newly mimicked data sets are safe to share with developers, QA, data scientists, and, heck,

00:44:30.720 --> 00:44:32.680
even distributed teams around the world.

00:44:32.680 --> 00:44:38.440
Shorten development cycles, eliminate the need for cumbersome data pipeline work, and mathematically

00:44:38.440 --> 00:44:41.340
guarantee the privacy of your data with Tonic.ai.

00:44:41.340 --> 00:44:47.760
Check out their service right now at talkpython.fm/tonic, or just click the link in your

00:44:47.760 --> 00:44:49.020
podcast player's show notes.

00:44:49.020 --> 00:44:54.380
Be sure to use our link, talkpython.fm/tonic, so they know you heard about them from

00:44:54.380 --> 00:44:54.660
us.

00:44:55.380 --> 00:45:02.020
I've got one more thing I want to talk to you about.

00:45:02.020 --> 00:45:13.840
One of the things, though, that you spoke about is the ability of having, and this is just

00:45:13.840 --> 00:45:15.800
generally true for SQLAlchemy.

00:45:16.340 --> 00:45:22.600
These models, they're tied back to what's called a session or a unit of work to do with the database,

00:45:22.600 --> 00:45:29.440
and you can't do a query, get a record, and then go from a separate situation and try to jam it back.

00:45:29.440 --> 00:45:33.200
It's got to be stuck to the session that it comes from, right?

00:45:33.600 --> 00:45:38.400
So you don't share the models across sessions, but one of the things that would be nice is just to have a single one.

00:45:38.960 --> 00:45:55.800
And so FastAPI has a dependency injection system that you talked about can be used for basically always providing one and only one session to an API endpoint or a web endpoint that then could be the database management.

00:45:56.600 --> 00:45:59.720
Like creating a unit of work that is the lifetime of the request, basically.

00:45:59.720 --> 00:46:00.780
Want to talk about that?

00:46:00.780 --> 00:46:01.360
Yeah, exactly.

00:46:01.360 --> 00:46:03.580
I think you described it perfectly.

00:46:03.580 --> 00:46:05.440
I don't know what else can I yell at the bullets, right?

00:46:05.440 --> 00:46:17.980
So this dependency injection system is just like you declare some function and FastAPI will make sure to run that function and provide the value to all the things that need that value for one particular request.

00:46:17.980 --> 00:46:18.400
Right.

00:46:18.400 --> 00:46:20.180
This has nothing to do with the database, by the way.

00:46:20.180 --> 00:46:20.860
This could be anything.

00:46:20.860 --> 00:46:22.580
It could be a login framework, whatever, right?

00:46:22.580 --> 00:46:23.020
Exactly.

00:46:23.020 --> 00:46:40.100
So this is very useful for doing logging, for doing authentication, for doing authorization with roles and whatnot, for doing logging or setting up things that load stuff to remote servers like Sentry or like Datalog or I don't know, for all those things that you need to do.

00:46:40.100 --> 00:46:48.760
And that can be, and that is, there is some logic that needs to be shared and that could run before the request is handled and maybe after the request is done.

00:46:48.760 --> 00:46:50.820
And then you can share this information.

00:46:50.820 --> 00:46:56.720
So in many frameworks, there's a concept of a middleware, which is something that runs before the request and after the request.

00:46:56.720 --> 00:46:59.740
But then you have to run this thing for every request.

00:46:59.740 --> 00:47:04.740
With dependencies, with this dependency injection system, you can define exactly where you want it to run.

00:47:04.740 --> 00:47:14.820
And you can define, I want this to be run with a group of endpoints or with a group of path operations, as I used to call them, for just one particular endpoint or for a bunch of endpoints.

00:47:14.820 --> 00:47:21.720
And with this system, you can extract and you can generate whatever it is that you need to generate for the particular request.

00:47:21.720 --> 00:47:34.300
And the good thing about the dependency injection system is that if you're extracting information from the request, for example, from a header, then this information will be also extracted and included with past API and with all the open API and all these standards.

00:47:34.300 --> 00:47:40.420
So you will get that information in the automatically generated user interface to explore the API.

00:47:40.420 --> 00:47:40.640
Yeah.

00:47:40.640 --> 00:47:41.140
Very cool.

00:47:41.540 --> 00:47:47.440
So what steps do I have to take to do dependency injection to get that session to show up?

00:47:47.440 --> 00:47:51.060
I remember you had it in the documentation, but I don't remember where it is right now.

00:47:51.060 --> 00:48:01.000
I think for the particular case of SQL model, even though FastAPI and SQL model are independent, are made to be very compatible with that independent.

00:48:01.000 --> 00:48:08.460
I have a lot of documentation about writing applications with FastAPI and SQL model in the SQL model docs.

00:48:08.460 --> 00:48:17.280
The way that you will handle a FastAPI dependency in general is that from FastAPI, you import this special function depends.

00:48:17.280 --> 00:48:21.440
And then you create some function that will return sole value.

00:48:21.440 --> 00:48:26.560
This function will have the same style as any other function that handles a particular request.

00:48:26.560 --> 00:48:32.260
So it can have some parameters with some types and that information will be extracted from the request.

00:48:32.260 --> 00:48:33.720
And it just returns something.

00:48:33.720 --> 00:48:35.880
This is just plain old function.

00:48:35.880 --> 00:48:39.240
And then you pass function is this is what will be the dependence.

00:48:39.240 --> 00:48:42.780
Then you pass that function as a parameter to depends.

00:48:42.780 --> 00:48:50.320
And then you put depends as the default value of sole parameter in your main function that is handling the request.

00:48:50.320 --> 00:48:50.740
I see.

00:48:50.740 --> 00:48:53.600
So you could say session equals like depends.

00:48:53.600 --> 00:48:56.000
Here's some function that will get called to create the session.

00:48:56.000 --> 00:49:02.480
Session equals depends and calling depends with the function that is named get session or something like that.

00:49:02.480 --> 00:49:05.720
Do you have a way to see both sides of that?

00:49:05.720 --> 00:49:06.840
With dependency injection.

00:49:06.840 --> 00:49:13.800
So does it just return the value or can you like create a session and then yield the value and then keep processing or something along those lines?

00:49:13.900 --> 00:49:14.740
Exactly like that.

00:49:14.740 --> 00:49:14.980
Okay.

00:49:14.980 --> 00:49:17.320
You can create a session that you can yield the value.

00:49:17.320 --> 00:49:24.380
And then after the request is done, you can continue doing more stuff after yielding that particular session.

00:49:24.380 --> 00:49:31.700
So you can create a session, give the session from the dependency, and then the main code that will handle the request will have that session.

00:49:31.700 --> 00:49:36.400
And then the same dependency can take care of closing the session after the request is done.

00:49:36.460 --> 00:49:36.820
Exactly.

00:49:36.820 --> 00:49:40.220
Try, yield the session, finally close it.

00:49:40.220 --> 00:49:43.000
Maybe like if there's no exception, commit it, something like that.

00:49:43.000 --> 00:49:43.400
Exactly.

00:49:43.400 --> 00:49:43.820
Okay.

00:49:43.820 --> 00:49:44.960
That's pretty flexible.

00:49:44.960 --> 00:49:45.480
Yeah.

00:49:45.480 --> 00:49:57.660
And in the main code that you have, you don't have to take care about creating the session or closing it or making sure that there are no exceptions because you can do all that stuff in the dependency and share that logic throughout your code.

00:49:58.060 --> 00:50:03.460
And the other thing is that dependencies can themselves depend on other dependencies.

00:50:03.660 --> 00:50:21.540
So you can create a dependency that gets the session for the database, and then you can use that in another dependency that gets the current user and extracts from the header or from the authentication token or whatever, extract the user ID, then gets the user ID from the database and then returns the current user.

00:50:21.540 --> 00:50:27.380
So that you have a whole dependency that just takes care of returning the current user and making sure that it's authenticated.

00:50:27.380 --> 00:50:34.760
And then in all your, and you can reuse that code in all your endpoints or in all the main functions that handle the requests.

00:50:34.760 --> 00:50:43.760
And all those functions will be able to just get the current user right away without having to have all the logic to extract the information, process the token, all the stuff.

00:50:43.760 --> 00:50:44.020
Sure.

00:50:44.020 --> 00:50:45.200
This is very neat.

00:50:45.200 --> 00:50:49.000
I haven't used this enough during my work with FastAPI, so I got to check this out.

00:50:49.000 --> 00:50:49.940
All right.

00:50:50.360 --> 00:51:01.080
Now, let's do a bit of a lightning round here of the Twitter questions, because I've seen some of these questions come up in the live chat, but I also think I pulled out these ones that I thought people put on Twitter that were pretty good.

00:51:01.080 --> 00:51:08.600
LeMatte Webster says, any plans to ramp or replace Alembic to make migrations more developer friendly?

00:51:08.600 --> 00:51:13.640
So first of all, migrations, you've got to keep your database in sync with your models.

00:51:13.640 --> 00:51:18.940
Otherwise, SQLAlchemy and hence SQL Model will freak out about that because it's going to be a problem.

00:51:19.180 --> 00:51:25.060
But Alembic is, while it works, is a little bit hard to say, like, here's all the models you need to pay attention to.

00:51:25.060 --> 00:51:26.620
And here's the scenario where you run.

00:51:26.620 --> 00:51:28.240
It's a little bit clunky.

00:51:28.240 --> 00:51:30.520
It works well, but it's not super smooth.

00:51:30.520 --> 00:51:32.480
And I think that's what Matt's asking here.

00:51:32.480 --> 00:51:36.340
Alembic is the official tool from SQLAlchemy to do the migrations.

00:51:36.660 --> 00:51:42.680
And because SQL Model is itself also just SQLAlchemy underneath, Alembic works with it perfectly.

00:51:42.680 --> 00:51:44.320
Alembic is a great tool.

00:51:44.320 --> 00:51:47.480
It's super advanced and helps a lot.

00:51:47.480 --> 00:51:50.480
It can even generate automatic migrations and things like that.

00:51:50.480 --> 00:51:55.240
I think the main problem with Alembic is that in some cases it's not as intuitive.

00:51:55.540 --> 00:51:59.900
So, yes, what I want to do at some point is to wrap a bit of Alembic.

00:51:59.900 --> 00:52:03.260
I will replace it because it's already doing a magnificent job.

00:52:03.420 --> 00:52:12.120
But, and it will be super difficult to write all that logic and all that work that Mike has been doing for a very long time with SQL Model and Alembic.

00:52:12.120 --> 00:52:20.800
I will wrap it and I will try to add a bit more of documentation to explain how to handle the simplest cases, which is the same that I'm doing with SQL Model.

00:52:20.800 --> 00:52:25.560
If you need something more confidence, that will probably just go Alembic directly or to SQLAlchemy directly.

00:52:25.640 --> 00:52:39.060
Related to this, not Twitter question, Michael question, do you have any thoughts about testing code using these models and stuff like fake data or mocking out the database beyond just the standard stuff you would do to SQLAlchemy?

00:52:39.060 --> 00:52:44.400
Like, is there anything special about SQL Model that makes testing it easier or different than SQLAlchemy?

00:52:44.400 --> 00:52:47.460
No, the testing will be pretty similar to SQLAlchemy.

00:52:47.460 --> 00:52:47.860
Pretty similar.

00:52:47.860 --> 00:53:05.100
Yes, I just have like a bunch of documentation of how to do testing and even how to do testing with FastAPI applications using SQLAlchemy and how to, for example, use SQL Lite database or testing that will be run on memory instead of the production database that will be a PostgreSQL or MySQL or whatever.

00:53:05.100 --> 00:53:08.860
Just change the connection string to the engine and let it go.

00:53:08.860 --> 00:53:09.520
Yeah, okay.

00:53:09.520 --> 00:53:14.780
And then make it run with a database in memory and then make it work correctly with threads and everything.

00:53:14.780 --> 00:53:23.360
So something like a pytest fixture that initializes the database, but you just use colon memory colon for the connection string so that it just goes away.

00:53:23.360 --> 00:53:24.500
Yes, exactly like that.

00:53:24.500 --> 00:53:26.120
It's documented exactly like that.

00:53:26.120 --> 00:53:27.520
All right, right on.

00:53:27.520 --> 00:53:28.020
All right.

00:53:28.020 --> 00:53:34.200
Ricky Lim says, would SQL Model be part of, should, could be part of the standard Python library?

00:53:34.200 --> 00:53:36.100
I have some thoughts on this, but I want to hear your thoughts first.

00:53:36.100 --> 00:53:39.380
I have some historical perspective on this, but I want to hear your thoughts on this.

00:53:39.380 --> 00:53:42.560
Okay, so it will not be part of the standard library.

00:53:42.920 --> 00:53:53.960
Ideally, it will not be part of the standard library because if it was part of the standard library, it will mean that it will be available in Python 3.13 or something.

00:53:53.960 --> 00:53:59.660
And the users of Python 3.10 right now will not be able to use it on one side.

00:53:59.660 --> 00:54:14.700
On the other side, having more stuff in the Python standard library adds more inconvenience and more burden to the core maintainers, to the core developers, Python, which makes it even more difficult for them to continue supporting Python and all the different versions.

00:54:14.960 --> 00:54:26.600
And it will also complicate things for Red Cannon that is trying to figure out a way to slim down Python so that you can, for example, run it directly on the web browser.

00:54:26.600 --> 00:54:26.600
Right.

00:54:26.600 --> 00:54:26.920
Right.

00:54:27.040 --> 00:54:28.300
How do we build it in WebAssembly?

00:54:28.300 --> 00:54:28.980
This doesn't help.

00:54:28.980 --> 00:54:29.720
Yes, WebAssembly.

00:54:29.720 --> 00:54:33.540
And you know, Red Cannon and Kristen Hanks have been doing like a very cool job.

00:54:33.540 --> 00:54:34.620
It's very exciting.

00:54:34.620 --> 00:54:38.480
A lot of that will probably require actually is leaning down a bit.

00:54:38.480 --> 00:54:39.180
Standard library.

00:54:39.180 --> 00:54:41.420
As far as I understand, but I'm not no expert.

00:54:41.420 --> 00:55:02.560
Yeah, I imagine a world where we have, I don't know what the right word for it is, but there's like a standard cross environment Python minimum set of language features of standard library where things like, you know, stuff that talks on the network or does UI things or whatever.

00:55:02.560 --> 00:55:10.300
We're just, that is not part of this like minimum subset of Python that we're guaranteed to have so that we can put it on WebAssembly.

00:55:10.300 --> 00:55:12.260
We can put it on mobile devices.

00:55:12.260 --> 00:55:13.520
We can put it on servers.

00:55:13.520 --> 00:55:20.860
And as long as you program to this minimum set, your place where your Python can exist is broader, you know, like MicroPython potentially.

00:55:20.860 --> 00:55:24.980
I think that that's the trend and not the trend towards putting more stuff there.

00:55:24.980 --> 00:55:25.860
Yeah, exactly.

00:55:25.860 --> 00:55:26.700
Absolutely.

00:55:26.700 --> 00:55:27.200
Absolutely.

00:55:27.200 --> 00:55:29.440
And it's fun that it's already happening.

00:55:29.440 --> 00:55:31.280
MicroPython is already that.

00:55:31.400 --> 00:55:35.500
It's just that it cannot say it's a standard Python because it has to link that a bunch of things.

00:55:35.500 --> 00:55:42.780
But being able to have a microcontroller and write code in Python that is running the microcontroller, that's amazing.

00:55:42.780 --> 00:55:43.520
That's mind blowing.

00:55:43.520 --> 00:55:51.120
Yeah, the most mind blowing thing for me is that you can hook a Lambda expression directly to a hardware interrupt.

00:55:51.120 --> 00:55:53.500
That is like, what?

00:55:53.500 --> 00:55:54.300
You can do what?

00:55:54.300 --> 00:55:55.160
That's amazing.

00:55:55.160 --> 00:56:01.500
The historical perspective that I want to bring up here is I believe the core developers actually considered this for requests.

00:56:01.500 --> 00:56:10.920
And they decided that, no, they're not going to put requests in the standard library to replace URL lib because it would limit requests' ability to grow.

00:56:10.920 --> 00:56:12.940
Like, changes could only come once a year.

00:56:12.940 --> 00:56:16.740
It couldn't come three times a week if there was important changes, right?

00:56:16.800 --> 00:56:19.300
Like, the speed of development would be hindered.

00:56:19.300 --> 00:56:20.120
So they said, you know what?

00:56:20.120 --> 00:56:21.100
No, we don't want to.

00:56:21.100 --> 00:56:21.280
Yeah.

00:56:21.280 --> 00:56:21.980
Plains to tell us.

00:56:21.980 --> 00:56:22.260
Yep.

00:56:22.260 --> 00:56:22.580
All right.

00:56:22.580 --> 00:56:32.860
Next, Dimitri Figo says, are you considering working on generating TypeScript declaration files based on what's defined on the FastAPI backend?

00:56:33.060 --> 00:56:37.500
That was that documentation I showed where it has the schema and all that and, like, the endpoints.

00:56:37.500 --> 00:56:37.920
Yeah.

00:56:37.920 --> 00:56:49.140
To make it, to explain it a little more, when you go to the automatic interactive documentation for the API, that is all based on this standard schema of the API called OpenABI.

00:56:49.140 --> 00:56:54.420
This is just a huge JSON that defines all the data shapes that you're using, all the endpoints, everything.

00:56:54.420 --> 00:57:01.900
But that same thing, because it's a standard, then you can use that same thing to generate code for clients that communicate with your backend.

00:57:02.280 --> 00:57:06.660
And in fact, there's a bunch of client generators for many languages, including TypeScript.

00:57:06.660 --> 00:57:07.900
And I have used them.

00:57:07.900 --> 00:57:08.920
I have used some of them.

00:57:08.920 --> 00:57:10.740
And they are actually very, very good.

00:57:10.740 --> 00:57:16.500
Like, you can achieve things like defining in the backend where the data shapes that you're using.

00:57:16.500 --> 00:57:18.060
Then you update something.

00:57:18.060 --> 00:57:20.980
And then you regenerate the client in the frontend.

00:57:20.980 --> 00:57:29.140
And now, after that, the frontend team will be able to have access to this new API endpoint with autocompletion in their editor and everything.

00:57:29.140 --> 00:57:31.300
It works very well.

00:57:31.460 --> 00:57:32.300
It's super exciting.

00:57:32.300 --> 00:57:37.760
I just haven't had the time to document, like, the whole recipe to make it work.

00:57:37.760 --> 00:57:38.840
But it's already there.

00:57:38.840 --> 00:57:39.580
It's already working.

00:57:39.580 --> 00:57:41.520
And it already does a great job.

00:57:41.520 --> 00:57:41.780
Yeah.

00:57:41.780 --> 00:57:45.840
Maybe somebody wanted to contribute a PR or do some help there.

00:57:45.840 --> 00:57:46.440
They could, right?

00:57:46.440 --> 00:57:46.740
Yeah.

00:57:46.740 --> 00:57:52.200
You know, like, even a blog post will just, like, that will be a lot faster to get out.

00:57:52.200 --> 00:57:53.600
And that will help a lot.

00:57:53.600 --> 00:57:54.500
And a lot of people.

00:57:54.500 --> 00:57:54.880
Indeed.

00:57:55.600 --> 00:58:00.560
Zach Kud says, I'd love to hear how you approach figuring out the integration with SQLAlchemy.

00:58:00.560 --> 00:58:02.060
I mean, we talked a bit about this.

00:58:02.440 --> 00:58:08.960
But any other lessons you've learned from basically getting in the middle of SQLAlchemy and all of that it does?

00:58:08.960 --> 00:58:09.320
Yeah.

00:58:09.320 --> 00:58:17.200
It's very interesting that SQLAlchemy was created at a time where, like, it was, I don't know, Python 2.something.

00:58:17.200 --> 00:58:19.420
There were no context managers.

00:58:19.680 --> 00:58:25.480
So that thing that you do with a blog that you say with something, something, as blah, blah, blah.

00:58:25.480 --> 00:58:30.100
And then inside of that report code, that was not available, but that didn't exist.

00:58:30.100 --> 00:58:32.140
SQLAlchemy was made before that.

00:58:32.140 --> 00:58:37.300
So SQLAlchemy had to do a lot of sophisticated tricks to make everything work.

00:58:37.300 --> 00:58:44.720
And then getting down inside of it and trying to understand a lot of things like, why is this thing doing this?

00:58:44.720 --> 00:58:45.780
I'm working like this.

00:58:46.100 --> 00:58:47.560
And it's because of those things.

00:58:47.560 --> 00:58:59.960
I think I ended up learning a lot about those little details and a lot about how classes work internally and how a class is an instance of what and all those things and how you can configure all that.

00:58:59.960 --> 00:59:07.860
But the idea with SQL World is to make it super easy for you to use it without having to deal with all the internal complexity.

00:59:07.860 --> 00:59:08.800
Because it's crazy.

00:59:08.800 --> 00:59:09.460
You don't have to know.

00:59:09.460 --> 00:59:11.220
Only you had to know and go through it.

00:59:11.220 --> 00:59:26.480
So I think it's worth pointing out, I did have Mike Bayer on the show recently to talk about SQLAlchemy 2.0 and how they're moving to have basically the client side view of that be everything as a context manager and sort of change that up a bit.

00:59:26.480 --> 00:59:31.180
So how close is this to the 2.0 model or is it the 1.0 model API?

00:59:31.180 --> 00:59:31.680
Yeah.

00:59:31.680 --> 00:59:37.500
So Mike Bayer did a lot of work to make the compatibility transition as easy as possible.

00:59:37.500 --> 00:59:45.540
And SQLAlchemy, the latest available version, which is 1.4, is compatible with the previous style and with the new style.

00:59:45.920 --> 00:59:51.680
So code that is written with the new style will be compatible with SQLAlchemy 2.0 whatever and above.

00:59:51.680 --> 00:59:54.300
SQL model is based on this new style.

00:59:54.300 --> 01:00:05.240
So if you, for example, if you have an old application with SQLAlchemy, the first thing that you will want to do is to migrate to SQLAlchemy 1.4 and make sure that it's compatible with the new style.

01:00:05.240 --> 01:00:07.560
And make sure that you don't have any warnings.

01:00:07.560 --> 01:00:09.740
That's the main thing that you will do to make sure that it's compatible.

01:00:09.980 --> 01:00:12.800
And then after that, you can migrate to SQL more.

01:00:12.800 --> 01:00:14.500
The migration is also super simple.

01:00:14.500 --> 01:00:17.280
It's just like changing some classes for type annotations.

01:00:17.280 --> 01:00:17.680
Yeah.

01:00:17.680 --> 01:00:18.880
Absolutely.

01:00:18.880 --> 01:00:21.160
There was a question.

01:00:21.160 --> 01:00:22.000
Yeah.

01:00:22.000 --> 01:00:31.240
So the question by Python at night was, what would the level of effort or benefit, if any, of converting SQLAlchemy models and schemas to SQL model?

01:00:31.240 --> 01:00:36.700
Sounds like the effort is small and the benefit is all the features we spoke about, right?

01:00:36.700 --> 01:00:36.940
Yeah.

01:00:36.940 --> 01:00:37.460
Yeah.

01:00:37.460 --> 01:00:38.660
Okay.

01:00:38.660 --> 01:00:39.900
Would you recommend it?

01:00:40.180 --> 01:00:42.400
I mean, people are using SQLAlchemy.

01:00:42.400 --> 01:00:45.660
They're like, you know, I'd really would like to have some of that Pydantic magic.

01:00:45.660 --> 01:00:50.320
When would you say, okay, the trouble of making a change is sufficient?

01:00:50.320 --> 01:00:53.320
So if it was me, I would just use it right away.

01:00:53.320 --> 01:00:56.520
Right now, it's in version 0.0 point something.

01:00:56.520 --> 01:01:01.540
I will release 0.1 point 0 once I have 100% of test coverage.

01:01:01.540 --> 01:01:05.120
Right now it's 97% just because I'm not freaked out of that.

01:01:05.120 --> 01:01:07.920
But like most of it should already work.

01:01:07.920 --> 01:01:09.340
It's actually very simple.

01:01:09.340 --> 01:01:17.020
And if anything, the work that it does is actually very small because all their needs is all done by SQLAlchemy.

01:01:17.020 --> 01:01:22.040
If anything went wrong, you could also just like switch back to SQLAlchemy directly.

01:01:22.040 --> 01:01:24.300
It's just that you will lose the benefits.

01:01:24.300 --> 01:01:24.660
Right.

01:01:24.760 --> 01:01:29.360
Basically change your class back to driving from SQLAlchemy based and you're good to go.

01:01:29.360 --> 01:01:29.640
Yeah.

01:01:29.640 --> 01:01:29.900
Okay.

01:01:29.980 --> 01:01:37.020
The benefit that you will get is auto-completion and inline errors everywhere where you are using these classes that you will normally not get.

01:01:37.020 --> 01:01:39.480
And the integration with response model.

01:01:39.700 --> 01:01:47.920
And the integration, yes, of course, integration with response model is that, yeah, actually that's a lot of code that you will save if you can share that with Bidantic and with SQL Mode.

01:01:47.920 --> 01:01:57.440
Just make sure that you follow all the information about how to pin and how to upgrade versions because it's very detailed how you should go about that.

01:01:57.440 --> 01:02:05.860
Because as things are still changing and as it still has like a little bit of extra testing to do, you should be careful about how to pin.

01:02:05.860 --> 01:02:08.200
Just not install like whatever version comes.

01:02:08.200 --> 01:02:09.840
Just make sure that you've been the right version.

01:02:10.260 --> 01:02:16.700
You have tests and then when you upgrade, you make sure that the tests are pausing and then you can upgrade the version.

01:02:16.700 --> 01:02:16.920
Yeah.

01:02:16.920 --> 01:02:17.760
Good advice.

01:02:17.760 --> 01:02:19.980
Already talked about that one.

01:02:19.980 --> 01:02:26.980
So Lado asks, are you going to stretch modern Python conventions from the backend part, which you already did?

01:02:26.980 --> 01:02:35.200
We talked about like using the types and stuff for model binding to Python challenging front end as well.

01:02:35.200 --> 01:02:39.980
Should we expect something like Reactipy, like React, but in Python?

01:02:39.980 --> 01:02:40.740
That will be it.

01:02:40.740 --> 01:02:45.140
Do you care about the front end in the sense that you have any intention to build stuff for it?

01:02:45.140 --> 01:02:53.400
Yes, I care a lot from then and I have actually, I think I started in that and I have worked with Angular, React, Vue.js and like all the stuff.

01:02:53.400 --> 01:02:58.440
I think it will be amazing to be able to write Python in, to write Python for front end.

01:02:58.440 --> 01:03:05.720
But if someone is going to make that happen at some point, they'll probably let Connor and Christine Haynes making WebOssently work for Python.

01:03:05.720 --> 01:03:05.960
Yes.

01:03:05.960 --> 01:03:06.500
Yeah.

01:03:06.500 --> 01:03:10.620
You need the runtime there first and then it'll go much more easily.

01:03:10.740 --> 01:03:21.980
I absolutely think that it is not quite negligence, but it's close that the browser makers don't package other runtimes that are WebAssembly compatible, right?

01:03:21.980 --> 01:03:36.780
Like they should go to Ruby, they should go to Java, they should go to .NET and they should go to Python and say, are you willing to provide us a runtime that does X, Y, and Z that we can integrate in a generic way that we can include in our browser?

01:03:36.780 --> 01:03:43.440
So you don't have to say, oh, well, you can't use these other advanced things because the WebAssembly download is 10 megs.

01:03:43.440 --> 01:03:53.040
Well, if Firefox, Chrome, and Safari all shipped, you know, five of those, the five most common languages as binaries, like you would just have it.

01:03:53.040 --> 01:03:54.500
And it would just be this WebAssembly.

01:03:54.500 --> 01:03:56.440
Why does this not happen?

01:03:56.440 --> 01:04:01.340
So, but that's the real problem to Reactipi is that that's not there, right?

01:04:01.560 --> 01:04:01.680
Yeah.

01:04:01.680 --> 01:04:02.120
All right.

01:04:02.120 --> 01:04:05.840
Quasi says, what is he doing to address the bus factor?

01:04:05.840 --> 01:04:19.340
That is, if you get hit by a bus and then related to that in the audience, Prashat Rana says, well, you include a moderator to the project so it can become a community driven project and there's less burden on you.

01:04:19.340 --> 01:04:22.920
I think those are kind of similar questions from a different perspective.

01:04:22.920 --> 01:04:23.280
Yeah.

01:04:23.280 --> 01:04:28.920
And so the thing is for most of these projects, like most of the work can already be done a lot by the community.

01:04:28.920 --> 01:04:31.640
It's not that the work is, cannot be done.

01:04:31.640 --> 01:04:36.040
I just want to simply enable a bunch of permissions to a lot of people.

01:04:36.040 --> 01:04:42.760
We just go and merge all requests that very quickly because I like to make sure that everything works.

01:04:42.760 --> 01:04:54.340
For example, yesterday, for yesterday's FastAPI release, it had like four approvals, the pull request, but still it had a couple of bugs and a couple of things that needed to be solved.

01:04:54.340 --> 01:05:00.880
And like, I need to make sure that the code quality is still kept and that everything is working correctly.

01:05:00.880 --> 01:05:05.100
So for now, I'm still like making sure that I can give each one of the pull requests.

01:05:05.100 --> 01:05:17.480
But if people went and checked those pull requests and reviewed the code and tested it and like made sure, hey, this is working and I'm using it and it's working in my application or things like that, that will, of course, help a lot.

01:05:17.480 --> 01:05:19.320
Of course, that will help a lot.

01:05:19.320 --> 01:05:19.820
That's great.

01:05:19.820 --> 01:05:21.560
Obviously, it's open source.

01:05:21.560 --> 01:05:22.620
People can fork it.

01:05:22.620 --> 01:05:23.620
They can run with it.

01:05:23.620 --> 01:05:26.960
Like if you actually got hit by a bus, I think FastAPI would keep going.

01:05:27.080 --> 01:05:31.220
There'll just be a figuring out of like, all right, well, where's it going to send her back around before it settles down?

01:05:31.220 --> 01:05:31.940
Yeah, exactly.

01:05:31.940 --> 01:05:33.000
Not that anybody wants to.

01:05:33.000 --> 01:05:38.820
I think these questions are a reflection of how significant the impact you're having on the community is, right?

01:05:38.820 --> 01:05:39.220
Yeah.

01:05:39.220 --> 01:05:42.240
And, you know, like it's, I find the bus factor fun.

01:05:42.240 --> 01:05:54.240
I have been wanting to write a blog post about that for a while because I think the bus factor is something that works a lot for investors or for like founders that are not developers.

01:05:55.020 --> 01:06:01.100
And they are like associating with someone that is the only one that knows the product, but they want to be the owners of half of it.

01:06:01.100 --> 01:06:03.680
And if this person dies, they just lose all their investment.

01:06:03.680 --> 01:06:05.560
But when you're working with one of the things.

01:06:05.560 --> 01:06:06.180
They can keep it going.

01:06:06.180 --> 01:06:06.440
Yeah.

01:06:06.440 --> 01:06:06.700
Yeah.

01:06:06.700 --> 01:06:07.060
Absolutely.

01:06:07.060 --> 01:06:11.740
Like, for example, many of the projects from Nicole is mainly Tom Christie, which is one person.

01:06:11.740 --> 01:06:14.880
The maintainer of Flask, which is huge.

01:06:15.100 --> 01:06:23.020
It's mainly David Lord, and he's just like suffering through all of it and through all the abuse of developers and doing like a lot of the work.

01:06:23.020 --> 01:06:26.260
Probably like, I don't know, see, he has like another contributor or something like that.

01:06:26.260 --> 01:06:29.920
In the case of FastAPI, there's people like Marcelo that is helping a lot.

01:06:29.920 --> 01:06:31.940
And even Samuel Copping is also helping.

01:06:31.940 --> 01:06:39.440
That, you know, helps a lot with keeping the community, maintaining it, and like doing all the work that is needed to be done underneath.

01:06:39.440 --> 01:06:43.000
That doesn't really affect how it's working, you know?

01:06:43.000 --> 01:06:51.840
Like, the fact that the repository is not in another GitHub owner, in a GitHub organization or something like that.

01:06:51.840 --> 01:06:53.920
It's just because it's easier to handle.

01:06:53.920 --> 01:06:56.960
But at the same time, there's a lot of people that are already contributing.

01:06:56.960 --> 01:07:01.780
And that's the work that actually makes maintaining it and sustaining it.

01:07:01.780 --> 01:07:02.100
Exactly.

01:07:02.100 --> 01:07:03.280
That's the stuff that matters.

01:07:03.280 --> 01:07:03.660
Yeah.

01:07:03.660 --> 01:07:04.500
Absolutely.

01:07:04.500 --> 01:07:08.440
Alupia, just by the way, out there also says, I just want to say thanks to...

01:07:08.960 --> 01:07:12.960
You, so, your work is so important and just great.

01:07:12.960 --> 01:07:14.960
So, yeah, definitely a lot of people out there loving it.

01:07:14.960 --> 01:07:16.460
Okay, let's keep going.

01:07:16.460 --> 01:07:18.440
Back to SQL model.

01:07:18.440 --> 01:07:19.540
Roadmap for the future.

01:07:19.540 --> 01:07:20.340
What are the plans?

01:07:20.340 --> 01:07:21.440
So, migrations.

01:07:21.440 --> 01:07:29.480
I want to have a small wrapper to have a command line interface built on top of typer so that you can get into completion in the terminal as well.

01:07:29.480 --> 01:07:32.040
To have migrations based on an embed.

01:07:32.040 --> 01:07:37.060
To documentation for using ASIC with SQL model.

01:07:37.520 --> 01:07:39.520
ASIC is already supported by SQLAlchemy.

01:07:39.520 --> 01:07:39.760
Yeah.

01:07:39.760 --> 01:07:41.140
And that's a new thing, right?

01:07:41.140 --> 01:07:45.700
That is, you've got to create now an ASIC client or an ASIC session rather.

01:07:45.700 --> 01:07:48.860
So, I think it is instead of a regular session in SQLAlchemy.

01:07:48.860 --> 01:07:52.700
But that's one of the 2.0 big changes that Mike just pushed out.

01:07:52.700 --> 01:07:54.200
So, that flows through?

01:07:54.200 --> 01:07:54.760
Yeah.

01:07:54.760 --> 01:07:55.120
Yeah.

01:07:55.240 --> 01:07:56.360
Like, you can already use it.

01:07:56.360 --> 01:08:00.460
In fact, there's people using it in applications, in production applications right now.

01:08:00.460 --> 01:08:02.740
But it's just that I don't have it documented yet.

01:08:02.740 --> 01:08:10.420
SQLAlchemy already supports both the normal, the blocking interface, the regular interface, and the ASIC interface.

01:08:10.660 --> 01:08:12.860
And you can already use it with SQL models.

01:08:12.860 --> 01:08:14.300
But I want to document all that.

01:08:14.300 --> 01:08:15.100
Right.

01:08:15.100 --> 01:08:22.840
So, what you should do is you should just change your dependency injection based on whether you have a DEF method or an ASIC DEF method in FastAPI.

01:08:22.840 --> 01:08:28.640
And either create an ASIC client or ASIC session rather, or regular session, and then boom, off you go, right?

01:08:28.640 --> 01:08:39.620
That is one of the things that I think is so smart about the design of SQLAlchemy, that SQL model in Arids, is that the thing that handles if it's ASIC or not is the engine, not the models themselves.

01:08:39.620 --> 01:08:43.580
So, you can use the same models even if it's ASIC or not.

01:08:43.580 --> 01:08:44.060
I agree.

01:08:44.060 --> 01:08:44.660
Very nice.

01:08:44.660 --> 01:08:47.620
Good question by Lars, but I'm going to keep going because we're short.

01:08:47.620 --> 01:08:49.800
David Smith asked, are there plans to add ASIC?

01:08:49.800 --> 01:08:50.120
Yes.

01:08:50.120 --> 01:08:52.520
I think the question is, are there plans to document ASIC?

01:08:52.720 --> 01:08:53.280
Yeah, exactly.

01:08:53.280 --> 01:08:54.220
At this point, right?

01:08:54.220 --> 01:08:59.100
We kind of touched on this one about whether you should use it for an existing, like, should you be migrating?

01:08:59.100 --> 01:09:00.140
And we touched on that one.

01:09:00.140 --> 01:09:02.620
Rainer, who I think I saw in the audience, yeah, earlier.

01:09:02.620 --> 01:09:03.280
Hey, Brandon.

01:09:03.280 --> 01:09:05.900
Asked, could you go ahead and make NoSQL model?

01:09:05.900 --> 01:09:09.560
This is a chance for me to mention Beanie out there.

01:09:09.560 --> 01:09:12.880
I wanted to kind of ask you if you'd had a chance to look at this.

01:09:12.880 --> 01:09:15.080
So, Roman Wright also was out in the audience.

01:09:15.080 --> 01:09:15.600
I saw them.

01:09:15.600 --> 01:09:22.500
So, Beanie is an ODM, like for MongoDB, but also basically very similar based on Pydantic.

01:09:22.600 --> 01:09:29.780
So, I thought it was a, this immediately came to mind when I thought about SQL model is like, well, here's the MongoDB version.

01:09:29.780 --> 01:09:30.180
Yeah.

01:09:30.180 --> 01:09:32.140
It also, you know, tries to do the same thing.

01:09:32.140 --> 01:09:34.420
Have you thought about a NoSQL story?

01:09:34.420 --> 01:09:35.320
Have you looked at Beanie?

01:09:35.320 --> 01:09:36.060
What are your thoughts here?

01:09:36.160 --> 01:09:36.340
Yeah.

01:09:36.340 --> 01:09:42.740
And like, I really like both, that there are two alternatives for MongoDB with Bynantic.

01:09:42.740 --> 01:09:43.420
One is Beanie.

01:09:43.420 --> 01:09:44.840
The other one is Odmantic.

01:09:44.840 --> 01:09:46.960
I think they are both doing a great job.

01:09:46.960 --> 01:09:53.660
I like this particular thing about Odmantic is that it uses the same style of the interface of SQLAlchemy.

01:09:53.920 --> 01:10:02.320
that the thing that decides if it's async or not is the engine and not the model, which means that it will probably be easier to implement.

01:10:02.560 --> 01:10:05.860
Because both Beanie and Odmantic, both are async.

01:10:05.860 --> 01:10:06.080
Yeah.

01:10:06.220 --> 01:10:15.600
Both having the engine being the one that is async or not will allow implementing a regular or blocking version of it.

01:10:15.600 --> 01:10:23.360
So that you could have MongoDB models that are shared and reused for async code and for blocking code in the same application.

01:10:23.360 --> 01:10:25.940
So you could migrate more slowly and things like that.

01:10:26.220 --> 01:10:32.380
But I think both are doing a great job and like have a very nice interface that is like very close to Python.

01:10:32.380 --> 01:10:32.740
Yeah.

01:10:32.740 --> 01:10:41.460
Just as a sidebar, I do wish it was easier in Python to convert an async call to a synchronous call, knowing that it would block.

01:10:41.460 --> 01:10:44.220
Just go like, okay, here's async method.

01:10:44.220 --> 01:10:52.200
If I could just go dot wait or dot result or something and just make it execute and basically stop the async running from there.

01:10:52.200 --> 01:10:54.300
Then you could just do the query like dot.

01:10:54.300 --> 01:10:54.680
Yeah.

01:10:54.680 --> 01:10:55.580
Give me the answer, right?

01:10:55.800 --> 01:10:57.180
Whereas it's a little more tricky.

01:10:57.180 --> 01:11:00.780
You've got to get it like in the loop and run the loop to completion and stuff like that.

01:11:00.780 --> 01:11:00.780
Yeah.

01:11:00.780 --> 01:11:05.500
But you know, like that's why I just built a async, which is built on top of any IO.

01:11:05.500 --> 01:11:12.280
And the idea is that you have this function asyncify and the function syncify just to do that.

01:11:12.280 --> 01:11:20.800
So you can pass one function that is async and it will be run inside of the main event loop in async way.

01:11:20.800 --> 01:11:23.540
Or you can say, hey, asyncify this thing.

01:11:23.540 --> 01:11:30.280
And it will run the blocking function in a thread pool so that it's not blocking the main event loop, all the stuff.

01:11:30.280 --> 01:11:33.240
But it's actually, the work is actually all done by any IO.

01:11:33.240 --> 01:11:40.380
It's again, just doing the same thing of getting type annotations and other completion and all the stuff on top of the thing that is doing the work.

01:11:40.520 --> 01:11:43.820
Yeah, I definitely came across this not long ago and it looks very exciting.

01:11:43.820 --> 01:11:45.120
And I wanted to talk to you about it.

01:11:45.120 --> 01:11:48.140
But as you can see, we're way over time already for just our main thing.

01:11:48.140 --> 01:11:49.000
So let's go back to it.

01:11:49.000 --> 01:11:52.280
But maybe next time you're on, we could just talk about async stuff all day.

01:11:52.280 --> 01:11:56.460
Mike out there asks, what is the risk of using it in production?

01:11:56.460 --> 01:12:01.860
The risk is the risk that you have for using any software in production.

01:12:01.860 --> 01:12:03.280
Let me maybe rephrase it.

01:12:03.280 --> 01:12:08.440
So what is the readiness for production, I guess, is probably what he was thinking.

01:12:08.440 --> 01:12:08.780
Yeah.

01:12:08.780 --> 01:12:12.320
So the thing is, most of the work is done by Pylandtica and SQLAlchemy.

01:12:12.320 --> 01:12:18.560
And they have been used for years and they are doing an amazing job and they are already used by a lot of tools.

01:12:18.560 --> 01:12:24.740
SQLModal only does like a little bit of extra stuff on top just so that you can need all the type annotations.

01:12:24.740 --> 01:12:26.280
Most of the work is done by those.

01:12:26.280 --> 01:12:31.040
The other thing is that the test coverage is at 97%.

01:12:31.040 --> 01:12:35.140
So you have some certainty that it's working as intended, at least.

01:12:35.140 --> 01:12:42.160
I want to have it at 100% and I want to have tests in continuous integration with several databases.

01:12:42.160 --> 01:12:44.980
Because right now the tests are only run in SQLite.

01:12:44.980 --> 01:12:50.900
But you know that all the SQL stuff is actually done by SQLAlchemy, which is already tested in all the databases.

01:12:51.120 --> 01:12:57.400
So, you know, like there's a trade-off of trying this thing that still has a little bit of extra stuff to do.

01:12:57.400 --> 01:13:02.040
Most of the extra things that I will do on top of SQLModal are actually documentation.

01:13:02.040 --> 01:13:04.020
Not that much whole changes.

01:13:04.220 --> 01:13:09.900
And the ability to flip from one to the other pretty quickly means if you had to say, oh, no, this is not working out.

01:13:09.900 --> 01:13:10.600
We're switching back.

01:13:10.600 --> 01:13:13.200
It's not like, oh, well, you're completely rewriting.

01:13:13.200 --> 01:13:13.520
Yeah.

01:13:13.520 --> 01:13:14.280
It's not that much work.

01:13:14.280 --> 01:13:17.640
You just have to make sure that you're in the versions and you upgrade correctly.

01:13:17.640 --> 01:13:19.080
But otherwise it should be.

01:13:19.080 --> 01:13:23.900
And you will get more certainty about the code correctness because you have all the type.

01:13:23.900 --> 01:13:24.700
So, yeah.

01:13:24.700 --> 01:13:24.940
Yep.

01:13:24.940 --> 01:13:25.580
All right.

01:13:25.580 --> 01:13:26.600
We got one question left.

01:13:26.600 --> 01:13:36.440
You have this ability to take what are often somewhat existing APIs and then improve them in ways that people really connect with.

01:13:36.440 --> 01:13:36.860
Right.

01:13:36.960 --> 01:13:41.660
Like FastAPI didn't start from open TCP socket and let's start from there.

01:13:41.660 --> 01:13:43.740
It started on top of Starlet, right?

01:13:43.740 --> 01:13:45.380
Like you already mentioned, Tom Christie.

01:13:45.380 --> 01:13:50.740
This is on top of two very important libraries that they should go together but didn't.

01:13:50.740 --> 01:13:56.060
So the question Pierre asks is, you know, how did you learn to sort of come up with APIs like you have?

01:13:56.060 --> 01:13:57.940
Like what's your recipe for building these?

01:13:57.940 --> 01:14:06.940
I think the thing is that I have been always trying to solve a problem and I have always trying to improve my developer experience.

01:14:06.940 --> 01:14:09.520
and to improve the way that things work for me.

01:14:09.520 --> 01:14:14.000
And like I have ended up just like trying to understand what is the best way to achieve those things.

01:14:14.000 --> 01:14:21.260
And at some point I ended up learning a little bit about type annotations and I realized that, hey, this can be super powerful.

01:14:21.260 --> 01:14:22.720
I can reuse it for different things.

01:14:22.720 --> 01:14:30.680
And after looking and looking for different frameworks that did what I wanted, I ended up like saying, okay, I just have to build this because it doesn't exist yet.

01:14:30.680 --> 01:14:34.920
But it was just like trying to achieve getting the thing that I want.

01:14:35.020 --> 01:14:42.080
For example, I wouldn't go and build a NoSQL model or MongoDB because there's already Beanie and Nolpatic.

01:14:42.080 --> 01:14:44.080
They are already solving the problem.

01:14:44.080 --> 01:14:46.520
I try to avoid building new things.

01:14:46.520 --> 01:14:53.920
But when there's the case that nothing is really something that I want to have, then I go and try to build it.

01:14:54.220 --> 01:14:58.980
The other thing is I think one of the main features of all these tools is just a good documentation.

01:14:59.180 --> 01:15:11.700
And I guess like the only thing about that is that I write it as I will have liked to learn those things when I was just starting and was struggling to understand what are all these things.

01:15:11.700 --> 01:15:17.160
And I always have in mind like how will that newbie learn these things and understand it.

01:15:17.160 --> 01:15:20.260
I guess that's probably the main thing that I'm trying to do.

01:15:20.260 --> 01:15:22.280
To make it as easy to use as possible.

01:15:22.280 --> 01:15:29.340
Yeah, I feel there's a strong blend of like, let's take the new things that are really useful that maybe not everyone's using and make them very accessible.

01:15:29.340 --> 01:15:31.680
Make them very easy and default and so on.

01:15:31.680 --> 01:15:31.920
Yeah.

01:15:31.920 --> 01:15:33.800
Yeah, that's the learning spirit, I think.

01:15:33.800 --> 01:15:34.380
Yeah, absolutely.

01:15:34.380 --> 01:15:35.200
All right.

01:15:35.200 --> 01:15:37.100
Well, that's all the questions.

01:15:37.100 --> 01:15:40.100
We've been going a little bit long, but I really appreciate the time.

01:15:40.100 --> 01:15:42.700
Let's just real quickly ask you the final two questions.

01:15:42.700 --> 01:15:43.440
I'll let you get out of here.

01:15:43.820 --> 01:15:44.120
All right.

01:15:44.120 --> 01:15:50.400
So if you're going to write some Python code, work on SQL model or something else, what editor do you use these days?

01:15:50.400 --> 01:15:57.700
I think the both main editors, Visual Studio Code and PyCharm are doing an amazing job at supporting all these tools.

01:15:57.700 --> 01:16:02.480
Right now, my main one is Visual Studio Code, but yeah, I will just one of those.

01:16:02.480 --> 01:16:02.860
Okay.

01:16:02.860 --> 01:16:03.600
Very good.

01:16:03.600 --> 01:16:03.900
Very good.

01:16:03.900 --> 01:16:06.160
And then notable PyPI packages.

01:16:06.160 --> 01:16:07.760
I feel like we touched on these a little bit.

01:16:07.960 --> 01:16:14.120
We just mentioned them both, Manthic and Beanie for doing the same SQL model stuff, but for MongoDB.

01:16:14.120 --> 01:16:14.940
Yeah, right on.

01:16:14.940 --> 01:16:15.380
I agree.

01:16:15.380 --> 01:16:16.100
Those are both great.

01:16:16.100 --> 01:16:21.380
All right, Sebastian, it was great to have you back and congratulations on SQL model.

01:16:21.380 --> 01:16:23.180
Maybe next time we'll talk async.

01:16:23.180 --> 01:16:23.660
What do you think?

01:16:23.660 --> 01:16:24.200
Awesome.

01:16:24.200 --> 01:16:24.800
Sounds great.

01:16:24.800 --> 01:16:26.280
Thank you very much, Michael, for inviting me.

01:16:26.280 --> 01:16:28.380
Thank you everyone for staying this long.

01:16:28.380 --> 01:16:29.200
Yeah, you bet.

01:16:29.200 --> 01:16:29.720
See ya.

01:16:29.720 --> 01:16:30.100
See ya.

01:16:30.100 --> 01:16:30.320
Bye.

01:16:30.320 --> 01:16:33.640
This has been another episode of Talk Python To Me.

01:16:33.640 --> 01:16:35.460
Thank you to our sponsors.

01:16:35.460 --> 01:16:37.060
Be sure to check out what they're offering.

01:16:37.060 --> 01:16:38.540
It really helps support the show.

01:16:38.540 --> 01:16:42.820
Datadog gives you visibility into the whole system running your code.

01:16:42.820 --> 01:16:46.640
Visit talkpython.fm/datadog and see what you've been missing.

01:16:46.640 --> 01:16:48.800
Go throw in a free t-shirt with your free trial.

01:16:48.800 --> 01:16:55.380
Tonic.ai creates quality test data that does not contain personally identifiable information.

01:16:55.380 --> 01:17:00.340
Your generated data sets are safe to share with developers, UA, and data scientists.

01:17:00.340 --> 01:17:05.520
Most importantly, they behave like production because they're made from production data.

01:17:05.840 --> 01:17:09.620
Check them out at talkpython.fm/tonic.

01:17:09.620 --> 01:17:11.280
Want to level up your Python?

01:17:11.280 --> 01:17:15.340
We have one of the largest catalogs of Python video courses over at Talk Python.

01:17:15.340 --> 01:17:20.520
Our content ranges from true beginners to deeply advanced topics like memory and async.

01:17:20.520 --> 01:17:23.180
And best of all, there's not a subscription in sight.

01:17:23.180 --> 01:17:26.080
Check it out for yourself at training.talkpython.fm.

01:17:26.320 --> 01:17:27.980
Be sure to subscribe to the show.

01:17:27.980 --> 01:17:30.760
Open your favorite podcast app and search for Python.

01:17:30.760 --> 01:17:32.080
We should be right at the top.

01:17:32.580 --> 01:17:41.420
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:17:42.360 --> 01:17:44.880
We're live streaming most of our recordings these days.

01:17:44.880 --> 01:17:52.720
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:17:52.720 --> 01:17:54.560
This is your host, Michael Kennedy.

01:17:54.560 --> 01:17:55.840
Thanks so much for listening.

01:17:55.840 --> 01:17:57.020
I really appreciate it.

01:17:57.260 --> 01:17:58.920
Now get out there and write some Python code.

01:17:58.920 --> 01:18:19.680
I really appreciate it.

01:18:19.680 --> 01:18:49.660
Thank you.

