# Python Pydantic Tutorial: Complete Data Validation Course (Used by FastAPI)

## Метаданные

- **Канал:** Corey Schafer
- **YouTube:** https://www.youtube.com/watch?v=M81pfi64eeM
- **Дата:** 27.10.2025
- **Длительность:** 1:29:25
- **Просмотры:** 53,540
- **Источник:** https://ekstraktznaniy.ru/video/11693

## Описание

In this video, we'll be learning how to use Pydantic, Python's most popular data validation library. Pydantic uses type hints to validate data at runtime, ensuring that the data coming into your application meets your expectations. We'll cover everything from basic model creation and field validation to custom validators, type coercion, nested models, and model configurations. We'll also see why Pydantic is so widely used in libraries like FastAPI, data processing pipelines, and AI tools. If you've ever struggled with messy manual validation code or data that isn't the right type or format, Pydantic will make your life much easier. Let's get started...

The code from this video can be found here:
https://gist.github.com/26fbfae9fb2ad293cc431530e8932855

Type Hinting tutorial - https://youtu.be/RwH2UzC2rIo
UV tutorial - https://youtu.be/AMdG7IjgSPM
Mutable Default Arguments video: https://youtu.be/_JGmemuINww
Regular Expressions tutorial: https://youtu.be/K8L6KVGG-7o
Python Object-Orien

## Транскрипт

### Segment 1 (00:00 - 05:00) []

Hey there. How's it going everybody? In this video, we're going to be learning how to use Pyantic. Pyantic is Python's most popular data validation library and it uses Typants to do this validation. So, if you're already using Typants in your code, then this should feel pretty natural to add to our projects. Basically, Pyantic's primary purpose is to ensure that the data coming into our application meets certain expectations. So, if you've ever struggled with data coming into your applications not being the right type or format, or if you've written a lot of manual validation code that's ugly and hard to maintain, then Pyantic is going to make all of this a lot easier for us. And Pyanic is going to be important for us to learn because it's being used in so many different libraries lately. So, for example, fast API uses this for validating API requests and responses. It's used in data processing to ensure clean data. And if you've worked with any AI tools, there's also Pantic AI and that allows us to build AI agents with structured and validated outputs. So really anywhere that you need to make sure that your data is what you expect it to be. Uh Pyantic is going to be our go-to. Now, since Pidantic uses Python type hints for validation, if you're totally new to types or static type checking in Python, I'd recommend watching my typing and type checking video first and then coming back to this one. I'll have that linked in the description section below if anyone is interested. But with that said, let's go ahead and get started. So, first, let's take a look at why Pyantic is so useful. We can do our own checks on our data manually. So why would we use something like paidantic instead of writing this from scratch? So I have a quick example here of what some manual validation might look like. So we have this create user function that takes a username, email, and age. And look at all the validation code that we're writing in here so far. Uh so we're checking if the username is a string. email is a string and if the age is an integer. So let me run this real quick and we can see how this works. So I'll run this. Let me make this output a little larger here. So first let me look at uh both of the test users that I created here. So we have user one create user. We pass it in a username as a string, an email as a string, and an age as an integer. For this second user, I passed in uh the username as a string, the email as none, and the age as a string. So we can see that it creates that first user successfully and printed that out. But when we try to create the second user with the invalid data, we get an error. Now it fails on the first validation error that it hits. So even though we have multiple problems with this data, uh the email is none and the age is a string. We have multiple problems there. We only find out about one error at a time. Right now it's reporting that the email is invalid. And if I was to go back and fix that invalid email error that it tells us about, then we'd hit that next problem that it didn't tell us about uh as soon as I rerun this. So going back and fixing those things one at a time isn't really a great user experience. Also, this is a simple example with just three fields that we're validating. Uh and look how much code that we have just for that small bit of validation. Imagine if we had a complex object with nested data, lists, uh, dates, and other types. That validation code would get extremely overwhelming and super hard to maintain very quickly. So, let me show you what this same validation would look like with paidantic. So, I'm going to uh just delete the manual validation that we had before. Actually, let me just comment that out. And now, let me uncomment it the uh pyantic code here. and save that. Okay, so this is some very simple pyantic code. Now, if you've ever used data classes, uh you can see that pyantic gives us syntax that looks very similar to a data class. But data classes don't actually validate anything at runtime. So, Pyantic gives us both the benefits of a nice syntax uh with type hints and it actually validates your data at runtime. And Pyantic will collect all of those validation errors at once. So we don't just get the first error like we saw in our manual validation example. So now instead of all of that manual validation, we have a class here that inherits from base model and then we just define our fields with type hints and that's it. Uh we can create the user. Uh pidantic automatically validates everything for us and then we are printing that out. So, let me go ahead and run this simple example here. And just like before, uh I'm using those same users that I had before where the first one is good and the second one is bad. And now we can

### Segment 2 (05:00 - 10:00) [5:00]

see here that the first user prints out successfully. On the second user, we can see that it's saying that we have two validation errors for the user. Uh one is the email, it should be a valid string. and uh the age integer and it also gives us a little bit of extra information there. So now let me uh minimize that a little bit. So now we're getting both of those validation errors at once and we didn't have to write uh any of the validation code ourselves. So that's just the bare minimum of what we can do with Pyantic. Uh there's so much more to cover, but before we get started, I wanted to show you what this looked like uh compared to trying to manually validating this stuff ourselves. Uh so now that we've seen that quick intro, let's start from scratch and see how we get this installed and then look at some more complex examples. So uh I can go ahead and just delete everything that I have here so far. And now if we were to install this, I'm just going to bring up my terminal here. Uh, installing this is pretty straightforward. If you're using pip, then you could say uh pip install pyantic. Um, now I'm going to be using uh UV for this tutorial. Uh, if you haven't watched my uh UV tutorial, I have a video on that as well, but that will just be uh UV add pyantic. Now, I've already got this installed, so it shouldn't really need to uh install anything else there. So, I'll clear that out. Now, one thing that I do want to point out is that we're going to be using Pyantic version two in this tutorial. And that's important because the transition from version one to version two was pretty significant. Uh, a lot of the method names changed. Uh, so for example, in version one, you would use uh DICT to convert a model to a dictionary, but in version two, that's now model dump. And in version one, you'd use JSON to convert to JSON, but now it's model dump JSON. And there's several others as well. So if you're following along with older tutorials uh or documentation, you might see some of those old method names. Uh and it's good to know which version uh they are using and which version you're using just so you don't get confused there. So let me go ahead and verify really quick uh the version that I have installed. If I just open up the Python interpreter here, uh, and then I import paidantic, then I can check out the version just by saying, uh, pyantic and checking out double version and run that. We can see that we are on 2. 12. 3. But as long as you are using uh, version two, then you should be able to follow along just fine with this tutorial. Uh, so go ahead and clear that out. Okay, so now that we have Pyantic installed and we verified that we're on version two, we can actually start diving into how to actually use this. And we already saw a quick example at the beginning, but now let's create our first proper pidantic model from scratch and really understand what's going on. So we already removed all the code that we had before. Um, so now let's just import what we need to import and that's going to be the base model. So I'll say from paidantic import and this is going to be base model and I'm also going to import uh validation error because we are going to use that in a bit as well. And while I'm importing some stuff here I'm also going to be using uh some datetimes for some of our fields later on. Uh so let me go ahead and import datetime as well. So I'll say from datetime import uh datetime. Okay. And don't worry about those rearranging there. I just uh it's how my editor is set up to where it auto sorts my imports. Okay. So now let's create our user model. To create a pyantic model, we create a class that inherits from base model. So let's create a user class. So we'll say class user and this is going to inherit from base model. And now we define our fields using type annotations. So let's start simple with just a couple of required fields. So let's add a username here and that'll be a string. And let's also add an email and that will be a string as well. And that's really all there is to it for a basic model. These are required fields because they don't have default values. Pyantic will require these to be passed in when we create a user instance. Uh, and these type hints aren't just documentation like they would be with a regular class or data class. Pyantic actually uses these to validate the data at runtime. So let me go ahead and create a user and show how this works. So we'll say user is equal to and let's just leave that empty for

### Segment 3 (10:00 - 15:00) [10:00]

now and we will print out that user. So if I just create an empty user there, then this won't be valid because like I said, if we don't have any default value for the username or email, then they're going to be treated as a required field. So if I run this now, then we can see here that this is saying that we have two validation errors for the user and that the username field is required and the email field is required. So now let me go ahead and fill those in with some um fields here. So I'll just uh use strings for both of those so that it's a valid user. So for the username I'll just say coreyms and then for the email I will say email is equal to uh corem shaergmail. com. Okay. So if I save that and run it now uh then we can see that works. So now let me add in a few more fields to show the different types of fields that we can have. So right now username and email are required fields but we can also have optional fields with defaults. So let me add a bio and an is active field with default values. So up here in our user class, I will go ahead and add a bio. And the bio is going to be a string. And we are going to set a default value here of just an empty string for the bio. And now we'll also have is active. And let's make this a boolean. And I'll set the default of that equal to true. So now since these have default values, then these are going to be optional fields. So, if I rerun this without adding any additional information to our user here that we're creating. If I save this and run it, then we can see that still works and it just used those default values here of bio equal to an empty string and is active is equal to true. Okay. But what if we want a truly optional value that isn't required and is none by default? So, for example, let's say that I have an optional full name field. uh it wouldn't make any sense to give someone a default full name. So, we'd want to set this to none by default. So, if I come up here and say full name and I say that this is going to be a string and set this equal to none. Now, this might look like what we want, but let's think about what we're doing here. We're immediately saying that our full name is going to be a string. uh but that its default value is none but none is not a string so that doesn't make any sense. So to do this we can use the union syntax to say that our full name can either be a string and then use that pipe character for the union syntax and say string or none and then the default is none. So you can see that it had that underlined there for a second because I have my type checker on. If you have your type checker on uh and do it this way, then it should stop yelling at you for that. Okay, so now let me add a few more fields that we uh might expect when creating a user. So we might have a unique ID for each user and I'll just set that to a required field that's an integer for now. So above our username here, I will say UID and I'm going to uh set that as an integer. Now sometimes in Python, you'll see people use ID as the field name. I don't like using ID because ID is a uh built-in keyword in Python. So I like to use something like UID instead. Okay. So what else might we want for a user? Uh, so we might want to track when a user was verified, but that might not happen right away. So I will have a field here called verified and that will be a datetime. And since this can be none by default, then I'll use a union there of none. Set that as none for the default. Okay. So now we have a mix of required fields, optional fields with defaults and optional fields that can also be none. So let me update our user here to include that required uh UID. And let's just set this equal to uh I think I said that was going to be an integer. Yep. So I'll just set that equal to 1 2 3. And let me go ahead and run this to make sure it still works. Okay, so that still works fine. Uh so now let's talk about accessing and modifying data. We can access fields using dot notation just like any other Python object. So far we've just been printing out the entire user here. Uh

### Segment 4 (15:00 - 20:00) [15:00]

but if I wanted to grab a specific field, then I could use that with the dot notation. So for example, if I wanted the username, then I could say user username. And if I save that and run it, then we can see that it's giving us that username back. Now, we can also use this uh to set specific fields. Now, one thing to be aware of is that model instances are mutable by default and also by default they don't revalidate uh when you change a field. So, for example, if I come down here and say user. bio is equal to 1 2 3. Now, if you remember, our bio was set to a string up here. Uh, so if I print out that bio and run that, then you can see that when we reassign this, it ran just fine. So, it's not revalidating on assignment, and that's the default. Now, you can change that behavior in the model configuration, and we'll look at that later in the tutorial, but just be aware that this is the default. uh changing fields after creation doesn't trigger revalidation. So let me go ahead and set that to a proper string now the way it should. I'll just say Python developer for our bio. Save that and run it. And we can see that works fine. So now let me show you a couple of useful methods for working with pyantic models. If we want to convert our model to a dictionary then we can use the model dump method. So instead of bio here if we do model_dump and that is a method. So we need to put a parenthesis after that to execute that. If I save that and run it then we can see that it gives us a dictionary representation of our model. Now this is a Python dictionary. If we wanted JSON instead then this is going to be model_dump_json. And if I save that and run it, then this is going to be a JSON string now. And just like when we're working with the JSON module, we can pass options to format this nicely. Uh here as well. Uh so for example, one of the arguments that this takes is an indent. So I could say indent is equal to two. If I save that and run it, then you can see that now our JSON string is nice and formatted uh with two spaces there. So those model dump and model dump JSON methods are really useful when you need to serialize your models for storage or sending over a network. And by serialize I mean converting your Python object into a simple format that can be easily saved to a file or sent to another system. Okay. So now let's look more into what happens when validation fails. Remember at the beginning of the video I showed that pyantic catches multiple errors at once. So let me demonstrate that. Uh I'll create a try accept block here and try to create a user with bad data. So let me close down that output for now. So let me put this inside of a try accept block. So I will indent that. And then we will accept and this is going to be a validation error. We imported that earlier and I'll just set that as E. And now we can just print out that error. Now the fields that we have in there right now are fine. Uh so that's not going to throw an exception there. Let me go ahead and I'll put our username as none. And I'll set our email equal to something like one two three. Save that. And that should go ahead and throw an error. Now, now you don't need to use a try accept block here, but I wanted to show you an example of using one because usually you'd probably want to handle these validation errors in some way. Uh in this example, I'm just printing out the error. Uh so here I am passing in actually let's go ahead and just do all the fields. Uh so let me also do a UID of 1 123 as a string there also so I can show you something u specific about that. So I'm passing in a string for the UID uh which should be an integer none for the username uh which is a required and should be a string and passing in an integer for an email which should also be a string. So let me go ahead and run this. Now, this is a bit interesting here because we only got two errors, not three. Uh, it's complaining about uh the username should be a valid string and the email being an integer when it should be a string. But notice that the UID is not in that error list. Uh, that's because Pyantic has type coercion enabled by default. So when we passed in this string of 1 2 3, it

### Segment 5 (20:00 - 25:00) [20:00]

automatically converted this to an integer of 1 2 3. So that's actually a valid conversion and didn't raise an error. But the email that we passed in as an integer that was not converted to a string. Now that might seem weird, but the reason for that is that it's extremely common to receive numeric data as strings, whether that's from JSON, form inputs, uh URL parameters, or whatever. It's not as common to expect a string and get an integer. So, it's actually nice to have some type coercions, and that's enabled by default. Uh but we'll later see in the video uh how you can make validation more strict if you want to prevent this type of coercion from taking place. Uh for now just know that by default pyantic will try to convert types when it makes sense to do so. If it doesn't make sense then you'll obviously get an error. So if I change this UID to test instead of 1 2 3 and rerun this then we can see that now we're getting three validation errors because it could not uh convert that to an integer. Now actually this would be a lot more uh clean here. It still get has a trace back there. That's just because I am trying to print this out afterwards. Uh let me comment out that code and rerun this. And we can see that is more clean. Now we can see that we get the three validation errors and it tells us exactly uh why each of these failed validation and even has links uh to their documentation if you need to see more about that. Okay. So now that we understand the basics of creating models, let's talk about the different types that we can use and how pi pyantic validates them. Pyantic supports all the standard Python types that you'd expect. uh things like strings, integers, floats, booleans, uh list, dictionaries, uh tupil, sets, and also datetime objects like datetime, date, time, and time delta, things like that. And we can use unions uh to say that a field can be one of several types. And we can use optionals to allow none values and uh like we've seen already. And we could even use literal types to specify exact values that are allowed. So to demonstrate some of these, let's create a new model. And I'll add a blog post model uh below our user class. So I'm just going to overwrite everything that we have so far. And now let's create a new class. So I will call this blog post. And we are also going to inherit from that base model. And now let's just add uh some things that we might expect in a blog post. So we're going to have a title. Whoops. Okay. So a title, that's going to be a string. We'll have a uh section for content that will be a string. We'll have view count that'll be an integer. We'll set the default of that equal to zero. We'll do an ispublished boolean here and set this equal to false by default. And now let's take a look at a couple of other types here. So now let's say that we want a list of tags. So for lists we can specify what type the items should be in that list. So the so if we had something like tags uh then this is going to be a list of strings. So to specify that we can say list and then within the brackets here say str for strings. Now I'll set a default here and let me set this default and then I will explain it afterwards. So I'm going to set a default field and this is going to be a default factory and that default factory is going to be a list. So first we can see that it's yelling at me here that field needs to be imported. So I will go up here and import field and that's from paidantic that we're importing that field. Okay. And now we have this default factory equal to list. So the default factory is simply a function that gets called uh to create a new default value each time you create an instance. So here every time we create a user it calls list to create a fresh empty list for each user. Default factories are super useful for a lot of things and we'll look at more complicated example right after this. Uh but for now we're just using it to create an empty list. Now in paidantic you can actually just use an empty list as a default uh instead of the default factory. So I could do something uh like

### Segment 6 (25:00 - 30:00) [25:00]

this just a default value of an empty list. Um but I'm not going to do that because I just don't want to teach any bad habits here. Um, in regular Python classes, uh, using a mutable default like an empty list is a problem because that same list object gets shared across all instances. Uh, data classes won't even let you do it and requires you to use a default factory. Also, so even though Pyantic handles that safely, uh, I'm just going to stick with using the default factory pattern here. Uh if you want to learn more about why mutable defaults are a problem in Python, I have a video on that and I'll leave a link to that video in the description section below. All right, so now let me show you another example of a default factory with a slightly more complicated default value. Uh let's say that we wanted a created at timestamp that defaults to the current time. Uh now you might think that you could just do something like this. So, we'll say that that's going to be uh datetime and that we could do datetime dot now and do UTC for the datetime uh or for the time zone I mean and I would also need to import UTC from the datetime library as well. Okay, so you might think that you'd be able to do something like this, but this wouldn't work the way that you'd expect. uh that would call datetime. now once when the class is defined not when each instance is created. So every blog post would have the same time stamp. Uh instead we need to use a default factory with a function that will be executed each time that we create an instance. So let me copy this. Uh so just like before we're going to use a field with that default factory and then again you might be thinking that we can just put here uh datetime. now let's do tz is equal to uh UTC. Now you might be thinking that we're good now since we're using that default factory. But this still isn't good because we're still executing our function here. So we need to somehow not execute our function but also set our time zone to UTC. Uh because again the way that we have this now all of our blog post will have the same timestamp since it will just run this function once when we define our class not the actual timestamp of when we create each post like we're hoping for. So there are a couple ways that we can fix this and do this. uh if you understand lambda functions then we can use one of these here. So we could just say that our default factory we need to pass in a function that is unexecuted. Right now we are executing that function. So that unexecuted function I could just have this be a lambda and then that lambda will run datetime. now uh tz is equal to UTC and lambda is just an anonymous function. So here we're saying that every time we create a blog post, we want the default factory to run this anonymous lambda function and when that lambda function is run, we wanted to calculate the current date time uh with the time zone of UTC and that will work uh just fine. You'll see a lot of people doing it this way. Uh so I wanted to show you this uh but there's actually another way to do this uh that some people might prefer and I'll show you that also just in case uh you might see that in some code as well and that other way is using the funk tools. parial. So let me import that. So here at the top uh we will say from funk tools and we will import partial. And now partial lets us prefill some arguments to a function. So it allows us to pass in a function and some arguments and it returns a new unexecuted function that is the original function with those specific arguments already set. So what I could say here is I could let me just cut that out. So I could say that our default factory is a partial and partial we want datetime. now to be the unexecuted original function. And now we can say what arguments we want set with that datetime. now uh function and we want the argument of tz equal to UTC. Actually that needs to be all caps. Okay. So what partial is doing here again is we are uh partial is going to return an unexecuted uh function that is the datetime. now function uh with tz equal to UTC and uh

### Segment 7 (30:00 - 35:00) [30:00]

ready to go. So that'll work also. And let's add a couple more fields here just so we can see some more examples. So uh here's an example of a union type. So let's say that we have an author ID and the author ID could either be uh you know something like user 123 or it could be an integer uh just like 1 2 3. So we can use a union type here and we could just say that this is going to be a string and then we'll use that pipe character which is that union and we'll put int as well. So it can be a string or an integer. And finally let me also show you literal types. So these are great when you have a field that can only be specific exact values. So let's say for a status field uh we maybe only want to allow you know a status of draft published and archived for our uh blog post status. But first I'll need to import literal and that is from uh the typing module. So we can say from typing import literal. And now I can create a new status field here. And this is going to be literal. And like I said for this we'll say that it can be a literal of draft or published if I can spell. And then let's do one for archived as well. And we'll set a default here. Uh the default let's set as draft. So now status can only be one of those three exact strings. If you p try to pass in anything else, then it'll fail validation. All right. So now let's create a blog post and show that all this works. Uh and we're getting to the point where I'm going to be creating a lot of objects to show how these things work. Uh so I don't want to waste your time by you watching me type all these out. So I have a snippets file here. Uh let me go ahead and grab these from my snippets file and then we'll go over them. Uh, all of the code that I'm using in this video, by the way, is going to be available on GitHub and I'll have a link to that in the description section below. But from my snippets file here, let me go ahead and grab this and then I will explain it. So, let's test this out. So, if I look at the blog post again, we can see that we only have three required fields here. Uh, so it is the title, the content, and the author ID here are the ones that do not have uh defaults. Now, let me go ahead and put all of those required fields together there. Now, sometimes I'll spread these out based on um, you know, just whenever I'm typing them uh, to kind of let you see a little bit better. But let me go ahead and clean that up and just put those all together there. So, a blog post has those three required fields. uh the title, the content, and the author ID. And I'm passing all those in. So, let's uh print this out and see if that works. Whoops. And I ran that uh without printing that out. So, let me print out that post that I created. And now we can see that works. Okay. So, we'll notice a few things here. So, our default values uh seem to work just fine. Our tags are an empty list and we also have a created at uh timestamp that was generated when we created this instance. And also you'll notice that uh since we said that our author ID could either be a string or an integer since we passed in an integer as a string it did not coersse that into an integer and just kept that as a string. uh which is fine. If we wanted it to try to coorse those values like that uh then we could have explicitly said that we only wanted integers but we said that strings were fine. So now that we've seen the basic types that we can use let's talk about adding constraints to our fields. So we've already been using field for things like default factory. But field can do a lot more than that. uh we can use it to add constraints like minimum and maximum values uh length requirements and patterns things like that. Now in Pyanic version two the recommended way to add these constraints is using the annotated type from Python's typing module. So let me add that to our imports. So up here at the top uh from the typing module we are going to import annotated. Okay. Oops. And I think I spelled that wrong. Yeah, that is annotated. And now let's update our user model here to add some constraints. So for the UID, instead of saying that this is just an integer, let's add a constraint that it has to be greater than zero. So to do this, I can use an annotated type here.

### Segment 8 (35:00 - 40:00) [35:00]

And within here, I can say that this is going to be an integer. And now we can add in our constraints. So to do this, I can say uh that we want a field that is GT greater than equal to zero. If you've never seen this annotated type before, basically it's a way to add metadata to our current type. So in this example, we're saying that we want an integer, but the metadata included with this integer is that it is a value greater than zero. And for username, let's add some length requirements. So let's say that we want a username to be between 3 and 20 characters. So here I could say that I want an annotated type and that's going to be a string. And now for the constraints here we can say that uh we want a field and the we want this to be the min length equal to three. And then we'll have a max length here and we'll set that equal to 20. So let's keep looking at some other examples here. Uh so for example uh let's say that we wanted an age. I can do an age and I'll do annotated on that as well. Let's say that we only want people greater than or equal to the age of 13 uh that can sign up. So I can say that this is going to be an integer. Again, we're going to have this be a field. And let's do this was greater than zero. I want this to be greater than or equal to. So that'll be G. So greater than or equal to 13 and let's do less than or equal to. We'll set that to I don't know 130 years old or something like that. So you can see the pattern here. We use GT for greater than uh GE for greater than equal to. It would be LT for less than and LE for less than or equal to. And for strings we have min length and max length. Uh now let's update our blog post model to add some constraints there as well. Uh so for the title, let's say that this is going to be annotated and it will be a string and we'll have this be a field with a min length of one and we'll set a max length on our title. Uh we'll have that be 200 characters. For our content, I'm just going to go ahead and copy this so I don't have to type it out again. for our content. Uh let's just have this be a minimum length. We'll say that the minimum length uh for the content has to be at least 10 characters. Now, we could keep adding constraints for a lot of these, but I think you all get the idea. Uh but let me bring uh let me add one more field here uh to show you how pattern matching with regular expressions. So, for our blog post here, let me add a slug. and the slug. Let's have this be uh annotated as well. And this is going to be a string. Now, a slug field for a URL, you usually want some URL friendly identifiers in here. So, let's say that a slug should only contain lowercase letters, numbers, and hyphens. To do this, I can say that the field is going to have a pattern. And for this pattern, we can set this uh to a regular expression. So I will have this be I don't know how familiar you all are with regular expressions, but basically this part here is the beginning. end. Uh and in between these we will have all lowercase letters uh all digits. So 0 to 9 and we will also have hyphens. And this plus here is just saying that we can have one or more of those. If you've never used regular expressions before, then I do have uh a separate video specifically on those. Uh I'll leave a link to that video in the description section below as well if you're in more interested in learning how these work explicitly, but I just wanted to quickly show that here uh to show that we can pretty easily add these to our validation as well. So now the slug has to match that regular expression pattern uh or the validation will fail. All right. So now let's test these constraints and see what happens when we try to create a user that violates them. So I'm going to comment out the blog post uh here before uh where we were printing this out. And so let me comment that. And now let's create a user. And again I'm going to grab this from my snippets. So, from my snippets file here, uh, I'm going to grab an invalid user and paste this in. So, here we have a UID set to zero, which violates our greater than zero constraint. We have a username that's only two characters, which is too short. And we have an age of 12, which is below our minimum of 13. So, let me save this and run it. And

### Segment 9 (40:00 - 45:00) [40:00]

let's see what we get here. And we can see that we're getting all three of these validation errors. It's telling us that the UID needs to be greater than zero. Uh the username should have at least three characters and that the age should be greater than or equal to 13. So Panic caught all those uh constraint violations. But not only that, but it gave us a clear error message of exactly what's wrong. So that's how you add constraints using field and annotated. Now this pattern is really flexible and you can combine multiple constraints on the same field and this is the more modern uh pyantic version two uh way of doing things. If you're looking at older code or tutorials you might see things like con stir or con int and I think that stood for constrained integer constrained uh string things like that. Those are now deprecated in favor of this annotated pattern that we're using here. Now, in addition to adding constraints manually with field like we've done here, Pideantic also provides a bunch of specialized types that uh have constraints built in. And these are really convenient for common uh validation scenarios. Let me pull up the documentation here really quick to show you what all is available. And I'll have links to these in the description section below as well. But if I look at these pidantic types, uh we can see here like there's a shorthand for positive int uh negative int. Uh let's scroll through. We can see all these on the right side here as well. Um but we have if I go down further, we have non- negative float, strict floats, things like that. Um here we have UIDs, so unique identifiers. You can see it even has things like file path and things like that as well. So a lot of these uh are built in here. Now a lot of these are just shorthands for the annotated field uh with um that we saw before. So for example if I look at this positive integer here we can see that this is basically uh just a annotated integer with greater than zero. That's what we saw before and there are a bunch of other shortorthhands like this. Uh so these save you from having to write all these out yourself. Um they also break these out into network types as well. So let me open this page also. We can see that we have network types here. And these contain a few others that we'll be using also. So some of the most useful ones that we'll be using from these two pages are things like email string for validating email addresses. Um HTTP URL for validating URLs. um the UID for unique identifiers and secret string for sensitive data like passwords and I can see that it's on this networking page is where they have the uh HTTP URL and also the email string down here as well. Now some of these require extra dependencies. So for example, email string requires the email validator library and when you install pyantic by default you just get the core library. Uh to get these extras you need to install them uh explicitly. So let me show you how to do that. So I'm going to install the email extra so that we can use the email string. So here in my terminal uh we can just say uv add and this is going to be paidantic and then in brackets we will say email. So if I run this then it should install all of that. Okay. And that would be the same with a pip install. So those brackets when installing uh packages with pip or UV are optional dependencies that can be installed with the package. You might see that from time to time and that's what that is. All right. So now that we have that installed, let's update our user model to use some of the special types. So first let me import uh what we need. So up here at the top let's add to our pyantic imports here. Um so I will import email str and also let's do http url uh secret string uh secret str and also I'm going to use uh uids for the unique identifiers. So that's actually going to come from the uid module. So I'll say from uyu id import uyu id and let's also import uyu ID4 is the one that we are going to use. So now let's update our user model. So instead of UID being a constrained integer here, let's make it

### Segment 10 (45:00 - 50:00) [45:00]

a UID with a default factory that generates a new unique ID for each user. So instead of this annotated here, I'm going to say that this is a UYU ID. And let's go ahead and have a default factory. And I will use that UU ID4 function that we imported. Uh we do not want to put the parenthesis because we don't want to execute that. We want the unexecuted function there as our default factory. And what that'll do is it'll generate a new unique ID for each user when we create one. Okay. So let's also change the email from a regular string to an email string. So here for email, I'm going to use that email stred. And I find that one super useful because we could try to come up with our own constraints or regular expressions to check for valid emails. But this just allows us to easily specify that we want an email string and paidantic does all that background work for us. So we don't have to worry about it which is super nice. And let's add a couple of other new fields here as well. Uh let's say that a user can have a website and for that we can use that HTTP URL that we uh had before. Now this will be optional. So I'll say that this can be an HTTP URL or none. set that equal to none by default. And let's also add a password field uh using that uh secret string that we imported. Uh so I can say password and that is just going to be a secret. Let me spell that correctly. Secret str. So the secret string type here is great for sensitive data because it will hide the password and logs and things like that when you print out the model. All right. So now let's test these special types. So let me remove that try except block that we had uh before and create a valid user here. Now I actually think I have this in my snippets file as well. Yeah, I do. Okay. So I have a valid user here. Let me grab this and I will just overwrite that try accept block there. Okay. So notice we're not passing in a UID anymore because that'll now be autogenerated and we're now passing in a password since it is required. Now I am getting a warning here. I think that is probably a rough warning uh that I have set up that just says that I'm probably hard- coding a password into my code. Uh that's fine. So I'll ignore that for now. So if I run this and it's printing out this user then we can see that the UID was generated for us and also look at the password here. It's showing as a secret string with these asterisk instead of the actual password. And if we dump the model to a dictionary or JSON uh the password will be hidden there too. But if we need to access the actual value then we can actually do that. All we'd have to do is say user. p password and then on that password we can use this get secret value method here. So if I save that and run it then you can see with that get secret value we actually do uh get that printed out. So it blurs out that password for security but if you need to grab it then it's easy to get that. So these special pyanic types are super convenient. Uh they give you complex validation with just a simple type annotation. Uh email string validates the email format. HTTP URL validates URLs and secret string protects sensitive data. And there are tons more in that documentation if you need them. So I'll be sure to leave uh links to these pages in the description section below because there's a lot of useful ones in there. So, now that we've seen how to use built-in constraints and special types, now let's talk about creating our own custom validators. Sometimes the built-in validation isn't enough, and you need to add in your own logic. Pyantic makes this really easy with decorators. So, let me import what we need for custom validators. Um, I'll add these to our Pyantic import. So right here at the end of our pyanic import, uh the custom validators I'm going to be taking a look at here. It's going to be field validator and we're also going to import model validator. And we are this validation info as well. And once I save that, my code's going to auto format here and put these on different lines since that's now getting a little long. Now, one thing to mention before we dive

### Segment 11 (50:00 - 55:00) [50:00]

into these is that in Pyanic version two, we use field validator and model validator. In version one, there was just a validator decorator. So, if you're looking at older tutorials or documentation, you might see the old validator decorator being used, but we're using the new latest decorators here. Okay. So, let's add a custom validator to our user model. So let's say that we want to validate that usernames only contain alpha numeric characters and underscores and we want to normalize them to lowercase. I'll add this method inside the user class. And I'm also going to grab this from my snippets as well and then we'll go over it uh really quick. Let me go ahead and clean up our user model here because I have all these on different lines so far. Okay. Now, this is going to be in my snippets as well. Let me grab these. Okay. So, I'll grab this and I will just put this after the fields in our user model and indent that. Okay. So, let me explain what's happening here. So, we use the field validator decorator and pass in the name of the field that we are going to be validating and that's going to be username. So this needs to be a class method also. So we add that decorator as well. We're calling this validate username. Since it's a class method, it takes the class first and then we are receiving this value v as a string and then we do our validation logic. So our validation logic here we are just uh saying okay we're going to get rid of underscores here and then check if that is an alpha numeric value and if it's not if that doesn't equal uh true then we're just going to raise a value error here that says our username must be alpha numeric uh underscores are allowed. Um, if everything goes well, then we're just going to return that alpha numeric value with underscores uh all lowercase. Really, the takeaway here for this specific pyantic video, I don't want you to get too bogged down in uh the exact checks that we're doing on the values here. Uh the what I want you to take away is that this is, you know, we're doing a check here. If something is wrong in our custom validator, we're raising a value error with a message. And if everything's fine then what we return here. So this is doing validation and normalization at the same time. We're normalizing this uh by returning all lowercase or it can be you know whatever it is that you want to do. So let me test this really quick. So I'll add some uh uppercase letters here to my username. So I'll say that the username is Corey_chafer. And now when I print this out, I no longer just want to get that password value. Uh let's print out the entire user. So if I run this, then we can see our username right here. So we can see that our username gets validated and also normalized to be all lowercase. Now we could have added this extra alpha numeric validation to our field section but I just wanted to show some extra validation here uh along with the normalization as well. So let me scroll back to our field validator. Now this is an after validation which is the default. That means that it runs after paidantic has already done its type checking and bas basic validation that we've had before. But sometimes you want to uh pre-process data before the main validation happens. For that you use the mode of before. Now let me show you an example with the website field. Uh but first let me show you how the current website validation works. So, let me add an invalid URL uh to my created user. So, down here, um let's also add a URL here. I'll say website is equal to uh corems. com. That might seem like it's valid, but if I save this and run it, then you can see that we get an error here. Now, the reason that this is an invalid URL is because it doesn't start with HTTP or HTTPS. Now, if I added that, then it would work just fine. So, I'm going to add a custom validator here so that if we pass in a URL without an HTTP or HTTPS, then we'll at least attempt to add that before the URL is validated. So, let me grab this from my snippets here. and then we will explain what is going on. So here after

### Segment 12 (55:00 - 60:00) [55:00]

this username validator here, let me make sure all my indents are right. Now this is similar to the one that we had before. We're saying that this is a validator for our website field. We're checking if there is a website value uh since this field can also be none. uh and if there is a value if it start doesn't start with an HTTP or HTTPS then we are just adding that in but remember I said that by default these custom validators are run after paidantic has already done its type checking and basic validation that we have above. So, this isn't going to work because it's going to do that HTTP URL validation before uh this custom one is even run. So, if I run this as it currently is, you can see that we're still getting the same error. So, what we need to do here is we need to pass in a mode of before. So here within our field validator decorator, I'm going to say mode is equal to and we'll set that equal to before. That will tell pyantic that we want to run this custom validation and modification before it does the HTTP URL validation uh that we have above. It checks if the value starts with HTTP or HTTPS. If not, it adds that to beginning uh and then when the HTTP URL validation runs, it'll get that properly formatted URL. So if I run this now, then we can see that this works uh and that we have a proper URL here. Now in addition to field validators, we also have model validators. These are useful when you need to validate multiple fields together or validate the complete model. Uh, so a super common use case for this is checking if a password and confirm password field match. So again, let me grab this as some code from my snippets and then we will take a look at all of this. So this is a good bit of code from my snippets that I'm grabbing here. But don't worry, I will uh explain all of this as we go. So let me comment out the user that we are creating here and let me paste in this new model here. So let me scroll up and I will explain this. So what we've done here is we've created a new model and it's just a simple user registration model. So all this has is an email that's set to an email string, a password here which is a string and then we also have a confirm password field here as well. I just put these to strings so that we can easily see this example. Now we also have a validator that checks if the password and confirm password fields match. Now model validators are a bit different here. Uh instead of just receiving the value, we're actually receiving self uh which is the whole model instance and we can access all the fields to do our validation. So this runs after all the fields have been validated individually. That's why we're not using the uh class method decorator anymore like we had with the field validators. So down here I'm creating a new user registration here. And we can see that we are creating an instance of a user registration that does not have matching passwords. So this has secret 123. This is secret 456. So let me run this and we can see that we get a validation error here that says value error uh passwords do not match. So using this model validator we were able to uh check both of those fields and compare those together. Okay. So now let me talk about some best practices for validators. So first you should always return uh the value even if you're not modifying it. So the validator should either return the value or raise an error. Uh second, you'll usually want to raise a value error for validation errors. Pyantic will catch that and convert it to a validation error automatically. You can see that what we're catching down here uh is a validation error, not a value error. Uh but it's going to catch this value error and turn that into a validation error automatically for us. And lastly, if you're going to raise an error, uh don't mutate the value first. So either return the modified value or raise an error but not both. So that covers some custom validators. So we've seen field validators for individual fields. Uh model validators for validating the complete model and how to

### Segment 13 (60:00 - 65:00) [1:00:00]

access other fields during validation. Uh these give you complete control over your validation logic uh when the built-in constraints that we saw before aren't enough. Okay. So now let's talk about computed fields. So sometimes you want to have fields that are calculated from other fields and you want them to be included when you serialize your model to a dictionary or JSON. So for example, maybe you want a display name that is generated from the first name and last name or you want to calculate if someone is an influencer based on their follower count. Uh, by the way, I hate the word influencer, but I just wanted something quick and easy for this tutorial. Uh I guess I could have called it silver user or whatever. Uh but let's ignore that. But anyways, Pyantic has computed fields for exactly this kind of purpose. So let me add computed field to our imports here so that we can see what this looks like. So from pyantic I'm going to import computed field. And now let's add some fields to our user model that we can use for computed values. And I'll add a first name, last name, and a follower count. Uh so let me grab these from my snippets here. So first I just have these new fields that I'm going to be adding in here. So for the user, let me add these here at the bottom so that we can see them and indent them correctly. And let me also remove full name here uh because we are going to use a computed field to compute a display name instead. And now let me grab these computed fields from our snippets and I'll add these to the end of our model. So, let me grab these and I will just add these to the end of our model here. Make sure that we're indented correctly. Okay, let's walk through these. So, this is a property decorator combined with a computed field decorator. Now, I should have mentioned this earlier, but the validator decorators that we saw before and this computed field decorator, these are specific to paidantic. I mean, we imported those. But the class method uh decorator that we saw before and this property decorator, these are related to Python classes themselves. They're outside the scope of this video. But if you don't know what those decorators mean or what they do, then I'd recommend watching my playlist on Python classes if you want to know uh exactly what those do. And again, just like everything else, I'll leave a link to that playlist in the description section below. Uh but for now, let's go over these computed fields. So we can see that our computed field here is display name. And what we're doing in here is if we have a first name and a last name, uh then we just return those together uh with a space in between. Uh if we don't have the a first name and a last name, then we just return the username. And because we use this computed field, uh this will be included when we serialize our model. And I also included a more simple one here just to really knock the point home. This one doesn't even have any conditionals or anything. It is uh purely just a computed field. So we can see that we have a computed field called is influencer and all it does here is it returns whether our follower count is greater than 10,000. Okay. So let's test these computed fields here. So uh let me I'm going to uh comment out our user registration here from before and let me uncomment where we are creating a user here. So right now if I run this as is uh then you can see that we have a display name here now and since we didn't have a first name or a last name that just returned our uh username and that's how we had that set up. Um and we can also see here that we have is influencer equal to false because our follower count by default is zero. uh but let me add in a first name and last name and we can see how that gets uh computed differently if we have those values. So I'll say first name is Corey. We will set a last name equal to Schaffefer. So now if I rerun this again then we can see that we now have those first name and last names in there and we have the display name that is basically that full name. So it's the first name and last name with a space in between. So these computed fields even though we didn't specify these fields

### Segment 14 (65:00 - 70:00) [1:05:00]

explicitly um since they are computed they get uh displayed whenever we serialize this data. So that's nice to have as an option. Um and these get computed from other fields. So, also if I wanted to update the follower count to 10,000 or so, then I could show you that and get that coveted influencer status, but I'm going to go ahead and skip past that. Now, like I said before, uh these computed fields will show up in our dictionary and JSON dumps as well. Uh so, let me dump this to JSON really quick. So, I'll say uh model dump JSON. And let's go ahead and set an indent of two because I want to have this formatted nicely here. So let's look at this. And like I was saying before, uh our uh computed fields of display name and isfluencer um they get added to our JSON output as well and also to the dictionary output if I was to just do a model dump. Okay. So another thing that I wanted to talk about uh was about using nested models. Now one of the more powerful features of pyantic is that you can use other pyantic models as field types. So you can have models within models and paidantic will automatically validate everything recursively. Now that might sound complicated but once you see an example of this it's super straightforward. So to demonstrate this, let me add in a simple comment model above our blog post model. So let me go up to our blog post here. And again, I'm going to grab this from my snippets here. So I have a very simple comment model here. So I will copy this and paste this in here. So for a comment, we just have some content, an author's email, and the count uh a likes count. So now let's update our blog post to use nested models. Right now we have author ID as just a string or an integer. We can see that here. But what if we wanted to store the full author information instead of just this author ID? Well, we can just use our user model as the type. So, instead of having author ID here as a string or an integer, I'm just going to call this author and we will just have this be a user type. So, now we'll have this uh full user object nested in our blog post. And let's say that a blog post is also going to have a list of comments. So to do this uh down here at the bottom I will add another field here called comments and let's have a list and this is going to be a list of comment which we created right here. Okay. And I'm also going to have this be a default factory of an empty list by default. And if you think about it, there's all kinds of different things that we could add here. So, we could even add a computed field like we saw before that counts the number of comments or something like that. Uh, but I'm not going to worry about adding that here. Uh, I'm just giving some ideas of what you could easily add to this uh to get some more useful functionality. Okay. So, now we have a blog post that contains a user as the author and a list of comments. So now let's create a blog post with all of this nested data. So instead of creating this with keyword arguments like we've been doing, let me instead create this from a dictionary to show what that might look like. Uh if you receive this data from an API or a form or something like that. So down here I'm going to comment out uh what we had before where we were creating this user. And now let me go ahead and make some space here. I'm also going to grab this from my snippets as well. This is a little bit longer here since we are creating a big dictionary of data here. Let me paste this in. Okay, so let me go over what this post data dictionary looks like. So we can see that we have uh we are creating a blog post here. So we have a title, content, a slug. All of that is perfectly fine and normal. Once we get to the author, we have a nested dictionary here. And this is going to be a user. So now we have a user here and it has to pass all the user validation checks. So we have a username of corems

### Segment 15 (70:00 - 75:00) [1:10:00]

email, age, password, things like that. For comments, we now have a list here. And this is a list of comments. And each one of these comments has to pass the comment validation. So for the comment validation, we have content, author, email, likes, uh, and I think that's all we had for comments. So now we're taking that post data dictionary and unpacking that to create a blog post. And Pideantic will automatically validate everything recursively. So, not only does it validate the blog post fields, but it also makes sure that our user and comment fields are good, too. Now, this is one way that you'll see it done, but if you're unfamiliar with unpacking like this with the asterisk uh or lists in Python, then another way that you can do this is by saying uh blog post. od model validate and then just pass in that directly without unpacking that. And those are essentially doing the exact same thing. They're just creating a blog post from our post data. So after we're creating that blog post, I am uh printing this out in a JSON format. So let's run this and see what this looks like. So we can see that works. Not only did it create our blog post, uh, but we also have all of this user information as well, uh, so we have the autogenerated, uh, unique ID for the author. Uh, there's the autogenerated uh, timestamp for our blog post that we have here. Uh, and all of the optional fields for all of these models. So nested models gives you a really powerful way to structure complex data with automatic validation all the way down. Okay, so we're just about finished up here. Uh but let's talk about some more advanced serial serialization and working with JSON. So far we've been using model dump and model dump JSON, but there are a lot more options for controlling how your data is serialized. One common use case is working with APIs where the external representation uh representation uses different field names than your internal Python names. So for example, maybe you're working with a JavaScript frontend that uses camel case, but you want snake case in Python. Or maybe you have internal fields that you don't want to expose when serialized. So to show you some of this, we're going to need to import config dict from paidantic. So let me go up here and do that. So from paidantic, we are uh importing config dict. Okay. And this is going to allow us to configure our models further. Uh let me actually copy that there. So for example, let's configure our user model. So to do this, we'll add an attribute to the model called model config. And we'll set this equal to that config dictionary there. And now we can configure our model by passing in arguments to this config dictionary class. Uh the first one that I'll set is populate by name. So that I will set that equal to true. And this tells Pantic to accept both the field name and an alias when loading data. So for example uh for an ID we are using UID for the field name which is usually what you want to do in Python because like I said before ID is a built-in keyword that we don't want to overwrite. But some of the data that we load in might actually use uh ID as the field name. So let's add that as an alias. So here at the beginning I'm going to say alias is equal to ID. So again quick recap there. So first we have this model config uh and we have this populate by name set to true. This tells paid to accept both the field name and the alias when loading data. So that allows us to be a little more flexible when loading or exporting data with different naming conventions. So, let me grab some more data to load in here. And all of our commented out code down here is getting a little messy. So, let me go ahead and just overwrite all of this right now. And then I will paste in some new stuff from our snippets here. Okay. So, from our snippets here, let me grab a little bit more here. Like I said, we're getting close to the end, so there

### Segment 16 (75:00 - 80:00) [1:15:00]

are no more snippets after this. Okay. Okay, so let me go over what we pasted in here. So you can see that the user data that we're loading in uh uses this ID field instead of UID like we have specified in our Python model. Uh but since we have model config with populate by name set to true and told that field uh the UID field to use ID as an alias, then I should be able to run this just fine. So if I save this and run it, then we can see that works and that we get that uh unique ID that we passed in there. Those match up. But you can also see that when it outputs the user, it's still using our field names. So it's still using uh UID here. So even though it was able to load in that data just fine, it's still outputting our own field names. And that might be what we want. uh but if you want it to use the aliases on output also then we can just specify that uh whenever we dump that out. So whenever I'm dumping this out here I'm already passing in an indent of two but I can also uh pass in a by alias equal to true. And now if I run this you can see that instead of UID we are getting ID. So when we're sending data to a front-end or external API, uh we can use the aliases to match their naming conventions. So now let's look at excluding and including specific fields. So sometimes you don't want to serialize everything. Uh maybe you have sensitive data or internal fields that you want to exclude. So for example, even though it's blurring out our password already, uh let's say that we don't want to include it in our output at all. Let's just exclude the password from serialization. So to do that, we can just say here with our when we are dumping this out, I will exclude that password and this is going to be in brackets here. So I will exclude that password. If I save that and run it, then you can see that we no longer have that password. It was excluded. Now, the inverse of that, if we wanted our output to be limited to only a few fields and we don't want to go through and exclude, you know, a whole bunch of fields and we just want to include some uh then what we can do is we can just be explicit about what we want to include. So let's say that I just wanted to include the username and email in our output here. What I could do is I could just say include and let's say that we just want the username and also the email. So if I save that and run it, then you can see that now all we have are those two included fields and everything else is excluded. Now, another thing that you might end up doing a lot is loading in data from JSON directly. So far, we've been creating users directly or loading them in from Python dictionaries. But let's create a user directly from a JSON string. So, to do this, I'm going to import the built-in uh JSON library here or the JSON module. So, I'll import that. Scroll back down here to the bottom. And now if our data was a JSON string instead of a dictionary, we could load that in with model validate uh JSON instead of just model validate. So model validate_json. And now this is going to be expecting a JSON string. Uh what I'll do here is I will just use that JSON module to dump our current dictionary to a JSON string. Now in a real world example, that JSON string would be coming from a different source because if you already had the Python dictionary, then you'd just load that in directly like we did before. Um so you wouldn't need to do this. Uh but if I run this just to show you that does work, uh we can see that validates just fine. So this model validate JSON method is really useful when uh you're receiving JSON data from an API or reading from a file. So these serialization features give you a lot of flexibility when working with external systems. Uh you can use aliases for different naming conventions. Uh control which fields get serialized with include and exclude and easily convert between your models and JSON. Now let's talk a little bit more about model configuration. So Pyantic gives you a lot of control over uh how your models behave through the model config attribute uh that we saw up here. Where

### Segment 17 (80:00 - 85:00) [1:20:00]

was that? It was in the user module. Okay, right here. So we've seen this a bit with the populate by name uh that we just now used. But there are a lot more options. And I should mention uh that in pyantic version one uh you would use the config class for this. But in version two uh we use this model config with config dict like we see here. But if you see in older code someone using the config class uh just know that that's the old way. Now one of the most common configuration options is controlling type coercion. Remember how pyantic normally coerces types uh like converting the string uh 39 to the integer of 39? Well, sometimes you want stricter validation where no type coercion happens at all. Uh to do this, it's just as easy as adding uh strict is equal to true to our config dict here. So, let me add that really quick. I'll say strict is equal to true. And let's put this on some new lines here so that we can see uh all of these configurations. So now the data that I've been using to create this user um has an age of 39, but it's coming in as a string. So pyantic has been converting this to an integer. But now with strict equal to true, it should reject this and tell us that an integer is required. So if I save this and run it, you can see that this string of 39 was working uh before. But now it's saying that our input should be an integer. So now uh we are in that strict mode. Now if I was to change this to be an integer of 39 instead. Save that and run it. Then you can see that works just fine. And also we're just printing out that username and email uh just so I can uh show all this. Let me go ahead and print out that entire user again. Okay, [snorts] there we go. So that strict mode is useful when you want to be very explicit about types and catch cases where you're receiving the wrong type of data. Uh you can also use uh strict types for individual fields like strict string or strict integer if you only want strict validation on certain fields rather than on the whole model. Now another useful configuration is the extra option which uh controls what happens when you pass fields that aren't defined in your model. Uh by default paidantic just ignores extra fields but we can change that. So if I wanted to be able to add extra fields, then I could uh come up here to our user model here and where we have our configurations, I can say extra equals allow. And now with the data I was loading in, if I add extra fields that aren't explicitly in our model, then it should accept them. Uh so let's add a note to our user here. So I'll just add in another field called notes and let's just set this equal to um I don't know kind of a Karen. So with extra equals allow that we have in our configuration any extra fields that we pass in uh should be preserved. So this can be useful for things like plug-in systems or when you want future compatibility where new fields might be added. Um, so if I run what we have here now, then we can see right here we have our extra field uh that was included even though that field's not explicitly uh in our user model. Now you could also set extra to forbid if you want to raise an error when extra fields are passed uh which is useful when you want to catch typos or ensure that you're only getting expected fields. Or you could use ignore which is the default behavior uh of paidantic where the extra fields are accepted but just silently ignored. And now another one that we can change here is validate assignment. So remember earlier when I mentioned that by default paidantic doesn't revalidate when you change a field after it's uh created. Well, you can change that within the configuration. So if I go up to the configuration here, if I add in uh validate assignment here and set that equal to uh true, then with this configuration set, it'll revalidate any time that we make changes uh which it didn't before. So if I go down here to the bottom after our user is created, if I change this user. e and change this to something that isn't

### Segment 18 (85:00 - 89:00) [1:25:00]

an email, so something like Corey M. Schaefer, if I run this now, then you can see that we are getting a validation error here uh because it is revalidating when uh on assignment whenever we change values. So that is not the default behavior of paidantic. You actually have to uh set that to have it revalidate. So that is super useful when you have models that uh get modified after creation and you want to ensure that they stay valid. And one more configuration that I want to show you here is frozen models. So sometimes you want to create immutable models where the data can't be changed at all after creation. So this can be useful for things like configuration objects or just when you want to ensure that data integrity and that nothing changes. So up here let me change this validate assignment setting and instead of validate assignment I'll set this as frozen equals true. So now if I rerun this I'm still going to get an error here. Uh but the error is going to be because the instance is frozen, not because the email doesn't validate. Even if the email validated. So let me do cordiumshaper 2@gmail. com. So that is a valid email. Even if the email is valid, if I run this, we're still going to get uh a validation error here because our instance is frozen and we can't make changes to it after it's been created. So that can be super useful if that's what you want and we uh and you don't want to make any changes once that's been created. And frozen models can also have uh a small performance benefit since Pyantic knows that the data isn't going to change. So these configuration options that we've seen, they give you a lot of control over how your models behave. So you can make validation stricter to prevent surprises. uh validate on assignment to keep uh data valid after it's created. Um control how extra fields are handled and make models immutable uh when you need that guarantee. Okay, so that covers everything that I wanted to look at for model configurations. So I know that we've covered a lot in this tutorial, but I hope that you see uh why pyantic is so useful for data validation and the flexibility that we have here. Uh, and the best part is that Pyantic does all of this with pretty minimal boilerplate. So you just add type hints to your models and Pantic handles the rest. And those type hints aren't just for validation. They also give you great IDE support uh with autocomplete and inline documentation. So your code almost becomes self-documenting. Now Pyantic also has a really strong ecosystem. So if you're interested in building web APIs, fast API uses paidantic under the hood for request and response validation. Uh SQL model which is a library for working with SQL databases. It also uses pyantic models. Uh so once you learn pyantic then you're going to see it pop up everywhere in the Python ecosystem. Uh, in the next videos I'm going to plan on covering fast API which will show you how Pantic is used in a realworld web framework. So be sure to subscribe so that you don't miss that. But I think that is going to do it for this video. Hopefully now you have a good idea of how to use Pyantic for its powerful data validation in Python. But if anyone has any questions about what we covered in this video, then feel free to ask in the comment section below and I'll do my best to answer those. And if you enjoy these tutorials and would like to support them, then there are several ways you can do that. The easiest way is to simply like the video and give it a thumbs up. Also, it's a huge help to share these videos with anyone who you think would find them useful. And if you have the means, you can contribute through Patreon or YouTube, and there are links to those pages in the description section below. Be sure to subscribe for future videos, and thank you all for watching.
