Modern APIs with FastAPI and Python Transcripts
Chapter: Modern language foundations
Lecture: Model validation, the hard way
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.