Skip to Content
Course content

332: Validating Data with Pydantic Models

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're running a high-end hotel. When a guest checks in, you don't just let them wander into a room. You have a front desk agent who checks their ID, verifies their reservation number, and ensures they've provided a valid credit card. If the ID is expired or the reservation number is missing a digit, the agent doesn't just "hope for the best"—they stop the process right there and tell the guest exactly what's wrong. They are validating the "data" of the guest before allowing them into the system.

In Python, Pydantic does exactly this for your data structures. Up until now, you've likely used type hints like name: str, but those are just suggestions. Python doesn't actually stop you from passing an integer into a string field at runtime. Pydantic turns those hints into actual enforcement. Here is how that maps to the code:

  • The Front Desk Agent: This is your Pydantic BaseModel. It's the logic that intercepts the data.
  • The ID/Reservation Check: These are your type annotations. If you say a field is an int, Pydantic ensures it's an integer (or something that can be safely converted to one).
  • The "Expired ID" Error: This is the ValidationError. Instead of your code crashing ten functions deep with a cryptic TypeError, Pydantic tells you exactly which field failed and why the moment the data hits the model.

Stop Trusting Your Input Data

I've spent way too many late nights debugging production crashes because an API I was consuming suddenly started sending null instead of an empty list. The "old way" of fixing this was writing a dozen if/else blocks to check every single field. It's tedious and it clutters your business logic.

With Pydantic, you define the "shape" of your data once. Check out this example for a user profile in a subscription app:

from pydantic import BaseModel, EmailStr, Field, ValidationError

class UserProfile(BaseModel):
    username: str
    email: EmailStr  # This specifically validates email formats
    age: int = Field(gt=0, lt=120) # Must be greater than 0 and less than 120
    membership_level: str = "Free" # Default value if not provided

# This works perfectly
try:
    user = UserProfile(username="coding_wizard", email="wizard@example.com", age=30)
    print(f"Welcome, {user.username}!")
except ValidationError as e:
    print(e)

# This will blow up (in a helpful way)
try:
    # age is too high, and email is invalid
    bad_user = UserProfile(username="chaos_monkey", email="not-an-email", age=150)
except ValidationError as e:
    print(e.json())

Adding Guardrails with Field

You'll notice I used Field in the example above. This is where Pydantic gets really powerful. While int tells Python the type, Field allows you to define the constraints.

I frequently use gt (greater than), lt (less than), min_length, and max_length. If you're building a system that accepts passwords, you don't just want a str; you want a str with a min_length=8. By putting these rules in the model, your actual application logic stays clean because you can assume that if a UserProfile object exists, the data inside it is already clean and valid.

Coercion: The Secret Helper

One thing that catches people off guard is "coercion." Pydantic isn't just a strict gatekeeper; it's a helpful one. If you define a field as an int but pass the string "42", Pydantic will realize it can be converted and will automatically turn it into the integer 42 for you. This is a lifesaver when dealing with form data or environment variables, which almost always arrive as strings.




📋 Practical Task

Build an E-commerce Product Validator

You are building a backend for an online store. You need to create a Pydantic model called Product that ensures no "garbage" data enters your database. Implement the following requirements:

  • product_id: Must be a string with a minimum length of 5 characters.
  • name: Must be a string.
  • price: Must be a float and must be greater than 0.
  • stock: Must be an integer and cannot be negative (0 or more).
  • category: Must be a string with a default value of "General".

Once you've defined the model, write a script that attempts to create two products: one with valid data and one with invalid data (e.g., a negative price or a product_id that is too short). Wrap the instantiation in a try/except ValidationError block to catch and print the errors for the invalid product.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.