Pydantic model_post_init() Method: Explained with Examples

Updated: December 1, 2023 By: Khue Post a comment

This succinct, practical article is about the model_post_init() method in Pydantic. We’ll learn the fundamentals of the method and then walk through a few examples of using it in practice. Without any further ado; let’s begin!

The Fundamentals

model_post_init() is a special method that gets called automatically after the initialization of a Pydantic model. The purpose of the model_post_init() method is to allow custom logic to be executed after the model initialization. This can be useful for performing additional validation, transformation, or computation on the model fields or attributes.

General syntax:

class MyModel(BaseModel):
    # define model fields and validators here

    def model_post_init(self, __context: Any) -> None:
        # write custom logic here

The method takes one parameter, __context, which is an instance of pydantic.context.Context class. This class provides access to some useful information about the model initialization, such as the input data, the config, and the fields1. The method does not return any value, but it can modify the model instance in place.

Enough of words. Now it’s time to get our hands dirty by writing some code.

Examples

Example 1: Basic

This example shows the basic usage of the model_post_init() method. What we’re doing here is to define a User model with a model_post_init() method that prints a message to the console when a User object is created.

The code:

# This example uses Pydantic 2.4
from pydantic import BaseModel


class User(BaseModel):
    name: str
    age: int

    def model_post_init(self, *args, **kwargs):
        print(f"User {self.name} has been created.")


user = User(name="Innocent Wolf", age=225)

Output:

User Innocent Wolf has been created.

Example 2: Validation in model_post_init()

This example demonstrates the way to perform additional validation in the model_post_init() method. We’ll raise a ValueError in the model_post_init() method if the user’s age is less than 18.

# This example uses Pydantic 2.4
from pydantic import BaseModel, ValidationError


class User(BaseModel):
    name: str
    age: int

    def model_post_init(self, *args, **kwargs):
        if self.age < 18:
            raise ValueError("User must be at least 18 years old")


try:
    user = User(name="Cute Puppy", age=2)
except ValueError as e:
    print(str(e))

Output:

1 validation error for User
  Value error, User must be at least 18 years old [type=value_error, input_value={'name': 'Cute Puppy', 'age': 2}, input_type=dict]

Example 3: Modifying Model Data in model_post_init()

This example shows how to modify the data of a Pydantic model in the model_post_init() method. We’ll use it to hash the user’s password after the model has been initialized.

# This example uses Pydantic 2.4
from pydantic import BaseModel, ValidationError, field_validator


class User(BaseModel):
    name: str
    age: int
    password: str

    @field_validator("password")
    def validate_password(cls, password):
        if len(password) < 8:
            raise ValueError("Password must be at least 8 characters")
        return password

    def model_post_init(self, *args, **kwargs):
        self.password = self.hash_password(self.password)

    @staticmethod
    def hash_password(password):
        # This is a simple hashing for demonstration purposes.
        # In a real application, you'd want to use a secure hashing algorithm.
        return "".join(reversed(password))


user = User(name="John Doe", age=35, password="123456789")
print(user.password)

Output:

987654321

Example 4: Setting a default value based on another field

In this example, we want to create a model that represents a person with a name and an email. We want the email field to be optional, but if it is not provided, we want to set a default value based on the name field. For example, if the name is Cristiano Ronaldo, the default email should be [email protected]. We can use the model_post_init() method to achieve this:

# This example uses Pydantic 2.4
from pydantic import BaseModel, EmailStr


class Person(BaseModel):
    name: str
    email: EmailStr = None

    def model_post_init(self, __context) -> None:
        # if email is not provided, set a default value based on name
        if self.email is None:
            self.email = f"{self.name.lower().replace(' ', '_')}@slingacademy.com"


# test the model
p1 = Person(name="Cristiano Ronaldo")
print(p1)

p2 = Person(name="Lionel Messi", email="[email protected]")
print(p2)

Output:

name='Cristiano Ronaldo' email='[email protected]'
name='Lionel Messi' email='[email protected]'

Conclusion

The model_post_init() method is a useful and powerful feature of Pydantic. It allows programmers to customize the model behavior and logic after the initialization, which can be handy for various scenarios. However, you should use it with caution and test it thoroughly, as it can potentially modify the model instance in unexpected ways or raise errors that are not caught by the validators.