mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
3dfae21208
* Support ORM objects to 'parse_obj', replace #520 * switch to GetterDict and orm_mode * tweaks * update docs * split tests and add @tiangolo's suggestion * split tests and add @tiangolo's suggestion * fix coverage
169 lines
3.4 KiB
Python
169 lines
3.4 KiB
Python
from typing import List
|
|
|
|
import pytest
|
|
|
|
from pydantic import BaseModel, ConfigError, ValidationError
|
|
from pydantic.utils import GetterDict
|
|
|
|
|
|
def test_getdict():
|
|
class TestCls:
|
|
a = 1
|
|
b: int
|
|
|
|
def __init__(self):
|
|
self.c = 3
|
|
|
|
@property
|
|
def d(self):
|
|
return 4
|
|
|
|
def __getattr__(self, key):
|
|
if key == 'e':
|
|
return 5
|
|
else:
|
|
raise AttributeError()
|
|
|
|
t = TestCls()
|
|
gd = GetterDict(t)
|
|
assert gd.keys() == set()
|
|
assert gd.get('a', None) == 1
|
|
assert gd.get('b', None) is None
|
|
assert gd.get('b', 1234) == 1234
|
|
assert gd.get('c', None) == 3
|
|
assert gd.get('d', None) == 4
|
|
assert gd.get('e', None) == 5
|
|
assert gd.get('f', 'missing') == 'missing'
|
|
|
|
|
|
def test_orm_mode():
|
|
class PetCls:
|
|
def __init__(self, *, name: str, species: str):
|
|
self.name = name
|
|
self.species = species
|
|
|
|
class PersonCls:
|
|
def __init__(self, *, name: str, age: float = None, pets: List[PetCls]):
|
|
self.name = name
|
|
self.age = age
|
|
self.pets = pets
|
|
|
|
class Pet(BaseModel):
|
|
name: str
|
|
species: str
|
|
|
|
class Config:
|
|
orm_mode = True
|
|
|
|
class Person(BaseModel):
|
|
name: str
|
|
age: float = None
|
|
pets: List[Pet]
|
|
|
|
class Config:
|
|
orm_mode = True
|
|
|
|
bones = PetCls(name='Bones', species='dog')
|
|
orion = PetCls(name='Orion', species='cat')
|
|
anna = PersonCls(name='Anna', age=20, pets=[bones, orion])
|
|
|
|
anna_model = Person.from_orm(anna)
|
|
|
|
assert anna_model.dict() == {
|
|
'name': 'Anna',
|
|
'pets': [{'name': 'Bones', 'species': 'dog'}, {'name': 'Orion', 'species': 'cat'}],
|
|
'age': 20.0,
|
|
}
|
|
|
|
|
|
def test_not_orm_mode():
|
|
class Pet(BaseModel):
|
|
name: str
|
|
species: str
|
|
|
|
with pytest.raises(ConfigError):
|
|
Pet.from_orm(None)
|
|
|
|
|
|
def test_object_with_getattr():
|
|
class FooGetAttr:
|
|
def __getattr__(self, key: str):
|
|
if key == 'foo':
|
|
return 'Foo'
|
|
else:
|
|
raise AttributeError
|
|
|
|
class Model(BaseModel):
|
|
foo: str
|
|
bar: int = 1
|
|
|
|
class Config:
|
|
orm_mode = True
|
|
|
|
class ModelInvalid(BaseModel):
|
|
foo: str
|
|
bar: int
|
|
|
|
class Config:
|
|
orm_mode = True
|
|
|
|
foo = FooGetAttr()
|
|
model = Model.from_orm(foo)
|
|
assert model.foo == 'Foo'
|
|
assert model.bar == 1
|
|
assert model.dict(skip_defaults=True) == {'foo': 'Foo'}
|
|
with pytest.raises(ValidationError):
|
|
ModelInvalid.from_orm(foo)
|
|
|
|
|
|
def test_properties():
|
|
class XyProperty:
|
|
x = 4
|
|
|
|
@property
|
|
def y(self):
|
|
return '5'
|
|
|
|
class Model(BaseModel):
|
|
x: int
|
|
y: int
|
|
|
|
class Config:
|
|
orm_mode = True
|
|
|
|
model = Model.from_orm(XyProperty())
|
|
assert model.x == 4
|
|
assert model.y == 5
|
|
|
|
|
|
def test_extra_allow():
|
|
class TestCls:
|
|
x = 1
|
|
y = 2
|
|
|
|
class Model(BaseModel):
|
|
x: int
|
|
|
|
class Config:
|
|
orm_mode = True
|
|
extra = 'allow'
|
|
|
|
model = Model.from_orm(TestCls())
|
|
assert model.dict() == {'x': 1}
|
|
|
|
|
|
def test_extra_forbid():
|
|
class TestCls:
|
|
x = 1
|
|
y = 2
|
|
|
|
class Model(BaseModel):
|
|
x: int
|
|
|
|
class Config:
|
|
orm_mode = True
|
|
extra = 'forbid'
|
|
|
|
model = Model.from_orm(TestCls())
|
|
assert model.dict() == {'x': 1}
|