4

Is it possible to create a Pydantic field that does not have a default value and this value must be set on object instance creation and is immutable from then on?

e.g.

from pydantic import BaseModel

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

user = User(user_id=1, name='John')
user.user_id = 2 # raises immutable error
KOB
  • 4,084
  • 9
  • 44
  • 88

4 Answers4

8

Pydantic already has the option you want. You can customize specific field by specifying allow_mutation to false. This raises a TypeError if the field is assigned on an instance.

from pydantic import BaseModel, Field


class User(BaseModel):
    user_id: int = Field(..., allow_mutation=False)
    name: str

    class Config:
        validate_assignment = True


user = User(user_id=1, name='John')
user.user_id = 2  # TypeError: "user_id" has allow_mutation set to False and cannot be assigned
ldrg
  • 4,150
  • 4
  • 43
  • 52
alex_noname
  • 26,459
  • 5
  • 69
  • 86
3

You can try my solution below:

from pydantic import BaseModel, Field
class User(BaseModel):
    user_id: int
    name: str
    def setattr(cls, name, value):
        if name == "user_id": # or add more fiels here
            raise AttributeError("Cannot modify {}".format(name))
        else:
            return type.setattr(cls, name, value)

user = User(user_id=1, name='John')
user.user_id = 2 # raises immutable error
Viettel Solutions
  • 1,519
  • 11
  • 22
2

If you want to make all fields immutable, you can declare the class as being frozen

class User(BaseModel):
    user_id: int
    name: str
    class Config:
        frozen = True

However, this will make all fields immutable and not just a specific field

Simon Hawe
  • 3,968
  • 6
  • 14
  • I am hoping just to have one field immutable. I am thinking of creating the immutable field as a private variable and then implementing a property.setter() mothod that raises an immatable exception – KOB Jan 10 '22 at 07:39
0

You now do this (in Pydantic v2) with the Field(frozen=True) kwarg (docs)

Immutability ¶

The parameter frozen is used to emulate the [frozen dataclass] behaviour. It is used to prevent the field from being assigned a new value after the model is created (immutability).

See the frozen dataclass documentation for more details.

from pydantic import BaseModel, Field, ValidationError


class User(BaseModel):
    name: str = Field(frozen=True)
    age: int


user = User(name='John', age=42)

try:
    user.name = 'Jane'  


except ValidationError as e:
    print(e)
    """
    1 validation error for User
    name
      Field is frozen [type=frozen_field, input_value='Jane', input_type=str]
    """
Louis Maddox
  • 5,226
  • 5
  • 36
  • 66