Advanced Pydantic Patterns
When Lesson 1 showed you basic Pydantic models, you learned that type hints document what your data should be. But what if your data has business rules that go beyond types? What if an email must have specific format? What if an age must fall between 13 and 120? What if two passwords must match?
This is where advanced Pydantic patterns come in. You'll move beyond simple validation to enforce your specific rules—the rules YOUR application needs, not just the rules Python's type system provides. You'll also learn the production pattern every modern Python project uses: pulling configuration from environment variables without hardcoding secrets or defaults into your code.
By the end of this lesson, you'll know exactly when to use custom validators versus simple Field constraints, and you'll be able to build production-quality configuration systems that validate data before your application even starts.
Section 1: Custom Field Validators
Beyond basic type checking, Pydantic lets you write custom validation functions. A field validator is a function decorated with @field_validator that runs when data enters your model.
Let's say you need to validate an email address. Python's type system knows an email is a string—but it doesn't know that a string should contain @, have a domain with a dot, and contain no spaces. You need to enforce YOUR specific email rules.
The @field_validator Decorator
Here's a User model with email validation:
Loading Python environment...
How it works:
- The
@field_validator('email')decorator tells Pydantic: "When someone sets the email field, run this function first" - The function receives the value being set (the email string) in parameter
v - Your function checks the rules. If something is wrong, raise
ValueErrorwith a clear message - If validation passes, return the (possibly modified) value
- On invalid data, Pydantic raises
ValidationErrorwith your custom message
💬 AI Colearning Prompt
"Show me 3 different ways to validate an email in Pydantic: basic @ symbol check, regex pattern matching, and checking against allowed domains. For each, explain when to use that approach and the tradeoffs."
Multiple Validators on One Field
You can attach multiple validators to the same field. They run in the order you define them:
Loading Python environment...
Each validator sees the output of the previous one. This pattern lets you build validation progressively, with each step handling one concern.
🎓 Expert Insight
In AI-native development, syntax is cheap—semantics is gold. You don't memorize regex patterns; you understand YOUR business rules and specify them clearly. AI generates the validator code; you validate the logic matches your requirements.
Section 2: Field Constraints
For simpler validation, don't write a whole @field_validator. Use the Field() function with constraints. Field constraints are built-in, optimized, and clearer for simple cases.
When to Use Field() Instead of @field_validator
Use Field() when you need:
- Minimum or maximum values (
ge=0for non-negative) - String length constraints (
min_length,max_length) - Pattern matching (
pattern=r"..."for regex) - Examples for documentation
Use @field_validator when you need:
- Complex conditional logic
- Checking against external data
- Cross-field relationships
- Custom error messages based on input
Field Constraints in Action
Loading Python environment...
Combining Field Constraints with Custom Validators
You can use both together. Field() handles simple structural validation; @field_validator handles complex logic:
Loading Python environment...
Field constraints (handled by Pydantic automatically) run first. Custom validators run after. This separation makes your code clear: simple rules → Field(), complex rules → validator.
Section 3: Cross-Field Validation with @model_validator
Sometimes a field's validity depends on OTHER fields. This is where @model_validator comes in. While @field_validator checks one field, @model_validator can see the entire model after all field validators pass.
Example: Password Confirmation
A common scenario: user sets new_password and must confirm it with confirm_password. These fields must match:
Loading Python environment...
Key difference: @model_validator(mode='after') runs AFTER all field validators. By the time your model validator runs, you know all fields have passed their individual validations.
🤝 Practice Exercise
Ask your AI: "Create a PasswordChange model where: new_password != old_password, new_password == confirm_password, and new_password must be at least 8 characters AND contain at least one number. Include helpful error messages for each rule."
Expected Outcome: You'll understand how to combine Field() constraints with @model_validator for comprehensive validation, seeing how different validation layers work together to enforce complex business rules.
Section 4: Settings Management with BaseSettings
Now you know how to validate data. The next level: validating CONFIGURATION. Every application needs settings (database host, API keys, debug mode).
The problem: where do you put these settings? Hardcoding is dangerous. Environment variables are common but error-prone. BaseSettings solves this: it reads configuration from environment variables or .env files, validates it with Pydantic, and makes it available to your application.
Why BaseSettings Instead of os.getenv()
Without Pydantic:
Loading Python environment...
With BaseSettings:
Loading Python environment...
Complete BaseSettings Example
Here's a realistic application settings model:
Loading Python environment...
Secret Fields: Hiding Sensitive Data
Notice Field(repr=False). This tells Pydantic: "Don't display this field when someone prints the settings object." This prevents accidentally logging API keys or passwords:
Loading Python environment...
Common Mistakes
Mistake 1: Overusing custom validators when Field() suffices
Loading Python environment...
Mistake 2: Not validating early (validate at boundaries!)
Loading Python environment...
Mistake 3: Hardcoding secrets instead of using BaseSettings
Loading Python environment...
Mistake 4: Forgetting the mode parameter in @field_validator
Loading Python environment...
Try With AI
Apply advanced Pydantic validation patterns through AI collaboration that builds production validation systems.
🔍 Explore Validation Strategy:
"Compare Field() constraints (structural: length, regex, range) versus @field_validator (logic: format, complexity) versus @model_validator (cross-field relationships). Show when to use each in a User model."
🎯 Practice Complex Validators:
"Build User model with: password requiring uppercase+lowercase+number+special char using @field_validator with regex, email rejecting 'test.com' domain, @model_validator ensuring password != username and password != email."
🧪 Test BaseSettings:
"Create AppSettings using BaseSettings that loads from environment variables with APP_ prefix, includes nested User validation, and uses env_nested_delimiter for nested fields. Test with .env file."
🚀 Apply Production Patterns:
"Design validation system showing: Field() for structure, @field_validator for complex rules, @model_validator for dependencies, all errors reported together, clear error messages. Explain when each validator type applies."
Time Estimate
35-40 minutes (7 min discover, 10 min AI teaches, 10 min you challenge, 8 min build)