notes on Pydantic
This commit is contained in:
parent
b0c1cab95a
commit
0790cd2218
1 changed files with 98 additions and 0 deletions
98
zk/Pydantic.md
Normal file
98
zk/Pydantic.md
Normal 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"}
|
||||||
|
|
||||||
|
```
|
||||||
Loading…
Add table
Reference in a new issue