Created
October 27, 2025 14:08
-
-
Save CoreyMSchafer/26fbfae9fb2ad293cc431530e8932855 to your computer and use it in GitHub Desktop.
Pydantic Tutorial Code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ### 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