Skip to content

Instantly share code, notes, and snippets.

@CoreyMSchafer
Created October 27, 2025 14:08
Show Gist options
  • Select an option

  • Save CoreyMSchafer/26fbfae9fb2ad293cc431530e8932855 to your computer and use it in GitHub Desktop.

Select an option

Save CoreyMSchafer/26fbfae9fb2ad293cc431530e8932855 to your computer and use it in GitHub Desktop.
Pydantic Tutorial Code
import json
from datetime import UTC, datetime
from functools import partial
from typing import Annotated, Literal
from uuid import UUID, uuid4
from pydantic import (
BaseModel,
ConfigDict,
EmailStr,
Field,
HttpUrl,
SecretStr,
ValidationError,
ValidationInfo,
computed_field,
field_validator,
model_validator,
)
class User(BaseModel):
model_config = ConfigDict(
populate_by_name=True,
strict=True,
extra="allow",
frozen=True,
)
uid: UUID = Field(alias="id", default_factory=uuid4)
username: Annotated[str, Field(min_length=3, max_length=20)]
email: EmailStr
password: SecretStr
website: HttpUrl | None = None
age: Annotated[int, Field(ge=13, le=130)]
verified_at: datetime | None = None
bio: str = ""
is_active: bool = True
first_name: str = ""
last_name: str = ""
follower_count: int = 0
@field_validator("username")
@classmethod
def validate_username(cls, v: str) -> str:
if not v.replace("_", "").isalnum():
raise ValueError("Username must be alphanumeric (underscores allowed)")
return v.lower()
@field_validator("website", mode="before")
@classmethod
def add_https(cls, v: str | None) -> str | None:
if v and not v.startswith(("http://", "https://")):
return f"https://{v}"
return v
@computed_field
@property
def display_name(self) -> str:
if self.first_name and self.last_name:
return f"{self.first_name} {self.last_name}"
return self.username
@computed_field
@property
def is_influencer(self) -> bool:
return self.follower_count >= 10000
class Comment(BaseModel):
content: str
author_email: EmailStr
likes: int = 0
class BlogPost(BaseModel):
title: Annotated[str, Field(min_length=1, max_length=200)]
content: Annotated[str, Field(min_length=10)]
author: User
view_count: int = 0
is_published: bool = False
tags: list[str] = Field(default_factory=list)
create_at: datetime = Field(default_factory=partial(datetime.now, tz=UTC))
status: Literal["draft", "published", "archived"] = "draft"
slug: Annotated[str, Field(pattern=r"^[a-z0-9-]+$")]
comments: list[Comment] = Field(default_factory=list)
class UserRegistration(BaseModel):
email: EmailStr
password: str
confirm_password: str
@model_validator(mode="after")
def passwords_match(self) -> "UserRegistration":
if self.password != self.confirm_password:
raise ValueError("Passwords do not match")
return self
user_data = {
"id": "3bc4bf25-1b73-44da-9078-f2bb310c7374",
"username": "Corey_Schafer",
"email": "[email protected]",
"age": 39,
"password": "secret123",
"notes": "Kind've a Karen...",
}
user = User.model_validate_json(json.dumps(user_data))
user.email = "[email protected]"
print(user.model_dump_json(indent=2))
### New BlogPost
post = BlogPost(
title="Getting Started with Python",
content="Here's how to begin...",
author_id="12345",
)
### Invalid User
try:
user = User(
uid=0,
username="cs",
email="[email protected]",
age=12,
)
except ValidationError as e:
print(e)
### Valid User
user = User(
username="coreyms",
email="[email protected]",
age=39,
password="secret123",
)
print(user)
### Username Validation
@field_validator('username')
@classmethod
def validate_username(cls, v: str) -> str:
if not v.replace('_', '').isalnum():
raise ValueError('Username must be alphanumeric (underscores allowed)')
return v.lower()
### URL Validation (no mode)
@field_validator('website')
@classmethod
def add_https(cls, v: str | None) -> str | None:
if v and not v.startswith(('http://', 'https://')):
return f'https://{v}'
return v
### Model Validator
class UserRegistration(BaseModel):
email: EmailStr
password: str
confirm_password: str
@model_validator(mode='after')
def passwords_match(self) -> 'UserRegistration':
if self.password != self.confirm_password:
raise ValueError('Passwords do not match')
return self
try:
registration = UserRegistration(
email="[email protected]",
password="secret123",
confirm_password="secret456"
)
except ValidationError as e:
print(e)
### Computed Fields
first_name: str = ""
last_name: str = ""
follower_count: int = 0
@computed_field
@property
def display_name(self) -> str:
if self.first_name and self.last_name:
return f"{self.first_name} {self.last_name}"
return self.username
@computed_field
@property
def is_influencer(self) -> bool:
return self.follower_count >= 10000
### Comment Model
class Comment(BaseModel):
content: str
author_email: EmailStr
likes: int = 0
### BlogPost Dictionary
post_data = {
"title": "Understanding Pydantic Models",
"content": "Pydantic makes data validation easy and intuitive...",
"slug": "understanding-pydantic",
"author": {
"username": "coreyms",
"email": "[email protected]",
"age": 39,
"password": "secret123",
},
"comments": [
{
"content": "I think I understand nested models now!",
"author_email": "[email protected]",
"likes": 25,
},
{
"content": "Can you cover FastAPI next?",
"author_email": "[email protected]",
"likes": 15,
},
],
}
post = BlogPost(**post_data)
print(post.model_dump_json(indent=2))
### User Dictionary
user_data = {
"id": "3bc4bf25-1b73-44da-9078-f2bb310c7374",
"username": "Corey_Schafer",
"email": "[email protected]",
"age": "39",
"password": "secret123",
}
user = User.model_validate(user_data)
print(user.model_dump_json(indent=2))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment