Modern APIs with FastAPI and Python Transcripts
Chapter: Modern language foundations
Lecture: Model validation, the hard way
Login or
purchase this course
to watch this video and the rest of the course contents.
0:00
The next important language future we're going to talk about is an external library for creating
0:04
classes, mapping them to submitted data,
0:07
doing conversions and validations, and proper error messages,
0:10
all that kind of stuff. But I want to start by looking at the hard
0:14
way, how we might do that without that library,
0:16
because it's easy to look at it
0:18
and go "well, that's kind of short and simple",
0:19
but if you see what it's actually doing for us,
0:22
well, then all of a sudden it becomes super awesome.
0:24
So let's do a real quick exploration of setting up some kind of class that does
0:28
validation in not the easiest way,
0:31
but very a natural way, you might expect. So I'm going to create a thing called "models" and in
0:35
here, I'm gonna put "orders_v1"
0:39
and the idea is that we're getting some kind of data submitted to us.
0:44
Usually this is a post or some sort of JSON body submitted to our API
0:49
in the terms of FastAPI,
0:51
but what I'm going to show you actually applies just generally. So you can use it
0:55
however, and whenever you'd like.
0:58
So what I have here is some typical looking data, it has an order,
1:01
it has an order item, created date,
1:03
it has some pages that were visited,
1:05
like so, if I want to do some kind of tracking,
1:07
like first they saw these pages,
1:08
then they decided to buy this thing.
1:10
Maybe I can optimize my funnel. And here's the price
1:14
in terms of dollars and cents.
1:16
What I want to do is I want to create a class called "order",
1:20
yeah? And I want to define some kind of initializer
1:23
where I can take an item ID, I can take a created date,
1:28
pages visited, and a price. Now PyCharm has this cool thing where it'll actually add
1:34
these fields for us if I just ask it to. So I'm
1:37
just gonna knock that out real quick.
1:41
Here we go. Well, is that good?
1:43
Uh, maybe. I don't know.
1:46
We want to created date to be a datetime,
1:48
right? So maybe even we want to specify that,
1:50
like, this is a "datetime.datetime" and pages visited is going to be a
1:55
list of integers, theoretically, import that. Price is easy,
2:00
that's just a float. And item ID is going to be an int. Now,
2:04
I know you're looking over the top and saying "that's a string" I know, I wanted
2:08
it to be an int. But remember,
2:09
the Internet submits things often as strings like query strings and so on.
2:13
And I don't want to care that how it gets submitted.
2:15
I want it to be an integer.
2:16
So this is looking good, right?
2:18
We can come down here and we can just do a override of the let's say,
2:23
the str representation and we're just gonna return string of self
2:28
dot dunder dict. So that's not beautiful,
2:30
but that will show us kind of the data that we have.
2:33
So let's go and actually try to create this.
2:35
I'm gonna create an order, and I have to pass in values,
2:39
right? If I don't, it's like missing values,
2:41
It's gonna crash. And I'm going to do a cool trick where I can take
2:44
a dictionary and put a double star in front of it,
2:47
and what is going to do is that's going to map the keys to keyword arguments.
2:51
So this is the same a saying "O equals order of let's say item ID
2:56
equals order JSON dot get item ID comma
3:02
get a date comma pages visited".
3:04
Yeah, so pretty cool. Let's do that.
3:07
And then let's just print the string representation.
3:11
Print out O. See what we get. Run this.
3:13
Cool. So, look, item ID is a
3:15
123, created days, fine, pages.
3:18
Wait a minute. Wait a minute. Wait a minute. Wait a minute.
3:19
Um, that's supposed to be an integer, right?
3:22
This is supposed to be a parsed datetime.
3:24
This is supposed to be a
3:26
list of integers. It is mostly but not 100%.
3:30
Look at that one. So we should have converted that number.
3:32
Okay. And we sort of have something of a representation,
3:36
but we could do better. So let me drop in something that actually does this
3:40
here. Because as you'll see,
3:42
it's not super fun to
3:44
write. Down here, we can go and we'll go through this beast.
3:48
Okay, So what I want to do is I want to add a real quick
3:52
requirement. We're gonna use "python dash
3:56
dateutil" like that and it is not misspelled.
4:02
And let's just install that, because that's gonna be a really nice way to parse
4:06
this string so we can get this out of
4:10
dateutil. Alright, well,
4:11
this sure looks more complicated. What do we got going on?
4:14
So our item is coming in and we're passing none.
4:17
A default value for pages visited.
4:19
Because if we pass immutable type,
4:21
it could get changed. It's like a big weirdness in Python.
4:24
So we're doing this trick, and then also,
4:27
we're converting this to an item ID,
4:29
but we don't want some kind of weird, cannot convert integer.
4:32
We need to have like a proper error message.
4:34
The item ID could not be converted to an integer,
4:37
so we're doing a try, except ValueError, raise a different exception,
4:40
Maybe this could also be a ValueError,
4:42
but right, that's not the point.
4:44
We've got to do a decent amount of work to get that parsed over correctly or
4:49
stop. created date we're gonna again parse it.
4:51
Here's the float. We're gonna make sure that that's a float and then check this out,
4:54
for the pages visited, we're gonna iterate each one and actually convert each individual one
4:59
to an integer. If none of,
5:01
there's one that's like some value that can't be an integer,
5:03
it's going to crash, I'm gonna raise that.
5:05
Maybe you also wanna have equals.
5:07
So we're gonna do that by checking the type and then comparing dictionaries.
5:12
But if you have an equals, you need a not equals,
5:14
we probably should add a hash,
5:15
which I haven't even bothered to
5:16
do. Does it still run? Let's try.
5:19
Hey, look at that. It actually works
5:21
great. So notice this. This is now an integer, this is now a
5:24
datetime, as you'd expect. This is now a list of actual integers, not
5:28
a mix of integers and strings or whatever the heck that was.
5:31
So Yeah, this works fantastically,
5:33
but look how gnarly it is.
5:35
Whoa. We thought there was like,
5:37
a nice, simple thing. No,
5:38
no, no, no, no,
5:39
no, not so quick. Not in the real world.
5:41
Not in the real world. So what I'm gonna show you is something that lets
5:44
us have even simpler than this,
5:46
and yet this cool validation that it's cool because it works and we don't have to
5:51
write it, alright? So this is the motivation.
5:54
Like, we want something that will take sort of semi-formed,
5:58
semi-corrected data and do all the conversions for us,
6:01
like we are down here. And yet we don't want, you know,
6:05
have it get these weird errors.
6:06
So we gonna do all this stuff manually,
6:08
right? Would be ideal to just not do that.
6:11
But this is probably what we really should
6:13
write if we're going to try to pass this arbitrary JSON data over to this
6:17
order and expect it to come to life.