from typing import List
from pydantic import BaseModel, ValidationError, conint
class Location(BaseModel):
lat = 0.1
lng = 10.1
class Model(BaseModel):
is_required: float
gt_int: conint(gt=42)
list_of_ints: List[int] = None
a_float: float = None
recursive_model: Location = None
data = dict(
list_of_ints=["1", 2, "bad"],
a_float="not a float",
recursive_model={"lat": 4.2, "lng": "New York"},
gt_int=21,
)
try:
Model(**data)
except ValidationError as e:
print("=== e.errors() ===")
print()
print(e.errors())
print()
print("==================")
print()
print("==== e.json() ====")
print()
print(e.json())
print()
print("==================")
print()
print("==== str(e) ======")
print()
print(str(e))
print()
print("==================")
上記を実行すると、結果は下記のようになります。
=== e.errors() ===
[{'loc': ('is_required',), 'msg': 'field required', 'type': 'value_error.missing'}, {'loc': ('gt_int',), 'msg': 'ensure this value is greater than 42', 'type': 'value_error.number.not_gt', 'ctx': {'limit_value': 42}}, {'loc': ('list_of_ints', 2), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'}, {'loc': ('a_float',), 'msg': 'value is not a valid float', 'type': 'type_error.float'}, {'loc': ('recursive_model', 'lng'), 'msg': 'value is not a valid float', 'type': 'type_error.float'}]
==================
==== e.json() ====
[
{
"loc": [
"is_required"
],
"msg": "field required",
"type": "value_error.missing"
},
{
"loc": [
"gt_int"
],
"msg": "ensure this value is greater than 42",
"type": "value_error.number.not_gt",
"ctx": {
"limit_value": 42
}
},
{
"loc": [
"list_of_ints",
2
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
},
{
"loc": [
"a_float"
],
"msg": "value is not a valid float",
"type": "type_error.float"
},
{
"loc": [
"recursive_model",
"lng"
],
"msg": "value is not a valid float",
"type": "type_error.float"
}
]
==================
==== str(e) ======
5 validation errors for Model
is_required
field required (type=value_error.missing)
gt_int
ensure this value is greater than 42 (type=value_error.number.not_gt; limit_value=42)
list_of_ints -> 2
value is not a valid integer (type=type_error.integer)
a_float
value is not a valid float (type=type_error.float)
recursive_model -> lng
value is not a valid float (type=type_error.float)
==================
パースを行うためのヘルパー関数
外部からのデータを pydantic のモデルにしたい場合、 パースを行うヘルパー関数が便利です。
ヘルパー関数には下記があります。
parse_obj : dict を引数としてモデルを作成します。
parse_raw : str や byte を引数としてモデルを作成します。
parse_file: File からモデルを作成します。Json などを読みたい場合に便利です。
詳しい動作はコード例をご覧ください。
import pickle
from datetime import datetime
from pathlib import Path
from pydantic import BaseModel, ValidationError
class User(BaseModel):
id: int
name = "John Doe"
signup_ts: datetime = None
# parse_obj
m = User.parse_obj({"id": 123, "name": "James"})
print(m)
# parse_objでエラーの場合
try:
User.parse_obj(["not", "a", "dict"])
except ValidationError as e:
print(e)
# parse_raw (json)
m = User.parse_raw('{"id": 123, "name": "James"}')
print(m)
# parse_raw (pickle)
pickle_data = pickle.dumps(
{"id": 123, "name": "James", "signup_ts": datetime(2017, 7, 14)}
)
m = User.parse_raw(pickle_data, content_type="application/pickle", allow_pickle=True)
print(m)
# parse_file
path = Path("data.json")
path.write_text('{"id": 123, "name": "James"}')
m = User.parse_file(path)
print(m)
上記を実行すると、結果は下記のようになります。
id=123 signup_ts=None name='James'
1 validation error for User
__root__
User expected dict not list (type=type_error)
id=123 signup_ts=None name='James'
id=123 signup_ts=datetime.datetime(2017, 7, 14, 0, 0) name='James'
id=123 signup_ts=None name='James'
再帰モデルの作り方
モデルは入れ子にして定義することができます。
下記コード例では、Spam モデルに Foo モデルと Bar モデルのリストが含まれています。
from typing import List
from pydantic import BaseModel
class Foo(BaseModel):
count: int
size: float = None
class Bar(BaseModel):
apple = "x"
banana = "y"
class Spam(BaseModel):
foo: Foo
bars: List[Bar]
m = Spam(foo={"count": 4}, bars=[{"apple": "x1"}, {"apple": "x2"}])
print(m)
print(m.dict())