Skip to main content

1. Why Pydantic?

While Python’s standard type hints and dataclasses help document and organize code, they do not enforce types at runtime. If you pass a string "100" to a dataclass attribute annotated as an integer, Python will allow it without raising any errors. To guarantee that data actually conforms to your types at runtime (e.g., when receiving request payloads from a client), we use Pydantic — the industry standard data validation library.

Dataclasses vs. Pydantic


Python Type Hinting & Generics

Before defining Pydantic models, it is essential to understand Python’s type annotations. Python uses Type Hints to declare the expected data types of variables, parameters, and class attributes.

Basic Type Hinting Syntax

To annotate an attribute, use the colon (:) syntax:

Type Hinting Generics (Collections)

Modern Python (Python 3.9+) supports generic type hinting for collections directly using built-in classes:
  • Lists (list[type]): Represents a list containing elements of a specific type.
  • Dictionaries (dict[key_type, value_type]): Represents a dictionary with specific key and value types.
  • Tuples (tuple[type1, type2, ...]): Represents a tuple with fixed positions and types.
  • Sets (set[type]): Represents a unique collection of items of a specific type.
Pydantic dynamically reads these standard annotations at runtime to parse and validate incoming data structure models.

2. Defining a BaseModel

To define a schema, create a class that inherits from pydantic.BaseModel.

3. Field Constraints (Field)

Field() is a helper function provided by Pydantic. It is used to define default values, validation rules, and metadata for model fields.

Required Field (...)

Use ... (ellipsis) to indicate that a field is required.

Optional Field (None)

Use None as the default value to make a field optional.

Default Value

Provide a default value that will be used if the client doesn’t supply one.

String Constraints


Numeric Constraints

Other useful numeric constraints:
  • gt → Greater than
  • ge → Greater than or equal to
  • lt → Less than
  • le → Less than or equal to

Pattern Validation


Description

Adds a description to the generated Swagger/OpenAPI documentation.

Example Values


Alias

Accepts a different field name in the request.
Request:

Complete Example

Commonly Used Field() Parameters

Quick Summary
  • Field(...) → Required field
  • Field(None) → Optional field
  • Field(default=value) → Default value
  • Use parameters like min_length, ge, gt, pattern, and description to validate data and improve API documentation.

4. Custom Validators (@field_validator)

For complex validation rules, use the @field_validator decorator:

5. Nested Models & Collections

Pydantic handles nested schemas, collections (list, dict, set), and unions (|) seamlessly.

6. Serialization & Deserialization

Pydantic provides easy built-in methods to convert your models back into dictionaries or JSON strings:

##Class Attributes in Dataclasses and Pydantic Models

Instance Attributes vs Class Attributes

Instance Attributes (Fields)

These are the attributes that represent the data of each object.

Dataclass

Usage:
Here, name and age are instance attributes.

Pydantic Model

Usage:
Again, name and age are instance attributes (also called model fields in Pydantic).

Class Attributes

Class attributes belong to the class itself rather than individual objects. For both dataclasses and Pydantic models, use ClassVar from the typing module to declare class attributes.

Class Attributes in Dataclasses

Usage:
Output
Notice that school is not part of the constructor.
Not

Class Attributes in Pydantic

Usage
Output
The class attribute is not included in the model fields.
Output

What Happens Without ClassVar?

If you omit ClassVar, the attribute becomes an instance attribute (field).

Dataclass

Now school becomes part of every object.
It also appears in the constructor.

Pydantic

Output
Since school is a model field, it is included in serialization.

Summary

Key Takeaways

  • Instance attributes store data for each object.
  • Class attributes are shared across all objects.
  • In both dataclasses and Pydantic, use ClassVar to declare class attributes.
  • Attributes declared with ClassVar:
    • Are not included in the constructor.
    • Are not serialized.
    • Are shared by all instances.
  • Without ClassVar, both dataclasses and Pydantic treat the attribute as an instance field.

Rule of Thumb

  • Use normal type annotations (name: str) for object data.
  • Use ClassVar for constants or values shared across all instances.

Practice & Exercises

To reinforce what you’ve learned in this section (defining BaseModels, Field constraints, custom validations, nested models, and serialization), practice with these interactive notebooks:

Follow-Along Practice

Practice defining Pydantic models, verifying coercion, handling ValidationErrors, setting Field constraints, creating custom field validators, nesting models, and serializing models.💻 VS Code | 🚀 Colab | 📥 Download

Practice Exercises

Test your knowledge with hands-on exercises including student GPA validator coercion, movies range length constraints, email domain field validators, and nested transaction schemas.💻 VS Code | 🚀 Colab | 📥 Download