notes on Pydantic

This commit is contained in:
Thomas Bishop 2026-01-09 09:18:51 +00:00
parent b0c1cab95a
commit 0790cd2218

98
zk/Pydantic.md Normal file
View file

@ -0,0 +1,98 @@
---
tags: [python]
---
Pydantic provides runtime type-checking and schema validation, similar to the
Zod library for TypeScript.
It integrates seamlessly with native Python [type hinting](/zk/Type_hinting.md).
## Basic usage
We define our model which must always inherit from the Pydantic `BaseModel`:
```py
class User(BaseModel):
id: int
name: str = 'John Doe'
signup_ts: datetime | None
```
Then we validate against data:
```py
external_data = {
'id': 123,
'signup_ts': '2019-06-01 12:22',
}
user = User(**external_data)
```
> Note here we can _set_ values when we define the schema, as with
> `name: str = 'John Doe'`.
## Extending schemas
In scenarios where you have common as well as variant values between different
schemas you can extend simply by passing the shared schema to each variant in a
composable manner.
```python
class CommonProperties(BaseModel):
category: str
meta_id: str
class VariantProperties(CommonProperties):
not_shared_field: int
```
> Note that the `BaseModel` must "drill-down"; it must be inherited at each
> stage.
## Aliasing
Sometimes the incoming data that you wish to validate will have field names
different to those that you want to use internally within your program.
You can use aliases to handle these values. This way, Pydantic won't error when
it receives them, whilst also representing the data in the format that you want
to use.
```py
class IncomingRecord:
sub_genres: Optional[str] = Field(default=None, validation_alias="subgenres")
```
This schema will accept both `sub_genres` and `subgenres` as the key for the
field but once instantiated, it will represent the field as `sub_genres`.
## Methods
You can add methods to the Pydantic schemas. These will execute whenever the
schema is instantiated. I used a method recently to transform the data of the
base schema into a dictionary that could be consumed by a particular API, e.g:
```py
class User(BaseModel):
id: int
name: str
@property
def to_alt_format(self):
alternative_id: self.id,
alternative_name: self.name
```
Then:
```py
raw_user_data = {"id": "1234", name: "Thomas"}
user = User(**raw_user_data)
print(user.to_alt_format)
# {"alternative_id": "1234", "name": "Thomas"}
```