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