Skip to main content

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:

  1. The @field_validator('email') decorator tells Pydantic: "When someone sets the email field, run this function first"
  2. The function receives the value being set (the email string) in parameter v
  3. Your function checks the rules. If something is wrong, raise ValueError with a clear message
  4. If validation passes, return the (possibly modified) value
  5. On invalid data, Pydantic raises ValidationError with 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=0 for 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)