mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
@@ -5,6 +5,8 @@ History
|
||||
|
||||
v0.2.0 (TBC)
|
||||
............
|
||||
* **breaking change**: ``values()`` on a model is now a method not a property,
|
||||
takes ``include`` and ``exclude`` arguments
|
||||
* allow annotation only fields to support mypy
|
||||
* add pretty ``to_string(pretty=True)`` method for models
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ m = Model(
|
||||
email_address='Samuel Colvin <s@muelcolvin.com >',
|
||||
email_and_name='Samuel Colvin <s@muelcolvin.com >',
|
||||
)
|
||||
print(m.values)
|
||||
print(m.values())
|
||||
"""
|
||||
{
|
||||
'cos_function': <built-in function cos>,
|
||||
|
||||
@@ -17,5 +17,5 @@ class Spam(BaseModel):
|
||||
m = Spam(foo={'count': 4}, bars=[{'apple': 'x1'}, {'apple': 'x2'}])
|
||||
print(m)
|
||||
# > Spam foo=<Foo count=4 size=None> bars=[<Bar apple='x1' banana='y'>, <Bar apple='x2' banana='y'>]
|
||||
print(m.values)
|
||||
print(m.values())
|
||||
# {'foo': {'count': 4, 'size': None}, 'bars': [{'apple': 'x1', 'banana': 'y'}, {'apple': 'x2', 'banana': 'y'}]}
|
||||
|
||||
+15
-7
@@ -1,5 +1,6 @@
|
||||
from collections import OrderedDict
|
||||
from types import FunctionType
|
||||
from typing import Any, Dict, Set
|
||||
|
||||
from .exceptions import Error, Extra, Missing, ValidationError
|
||||
from .fields import Field
|
||||
@@ -96,7 +97,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
Config = BaseConfig
|
||||
|
||||
def __init__(self, **values):
|
||||
self.__values__ = {}
|
||||
self.__values__ = OrderedDict()
|
||||
self.__errors__ = OrderedDict()
|
||||
self._process_values(values)
|
||||
|
||||
@@ -111,9 +112,16 @@ class BaseModel(metaclass=MetaModel):
|
||||
setattr(self, name, value)
|
||||
self.__values__[name] = value
|
||||
|
||||
@property
|
||||
def values(self):
|
||||
return dict(self)
|
||||
def values(self, *, include: Set[str]=None, exclude: Set[str]=set()) -> Dict[str, Any]:
|
||||
"""
|
||||
Get a dict of the values processed by the model, optionally specifying which fields to include or exclude.
|
||||
|
||||
This is NOT equivalent to the values() method on a dict.
|
||||
"""
|
||||
return {
|
||||
k: v for k, v in self
|
||||
if k not in exclude and (not include or k in include)
|
||||
}
|
||||
|
||||
@property
|
||||
def fields(self):
|
||||
@@ -175,7 +183,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
@classmethod
|
||||
def _get_value(cls, v):
|
||||
if isinstance(v, BaseModel):
|
||||
return v.values
|
||||
return v.values()
|
||||
elif isinstance(v, list):
|
||||
return [cls._get_value(v_) for v_ in v]
|
||||
elif isinstance(v, dict):
|
||||
@@ -194,9 +202,9 @@ class BaseModel(metaclass=MetaModel):
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, BaseModel):
|
||||
return self.values == other.values
|
||||
return self.values() == other.values()
|
||||
else:
|
||||
return self.values == other
|
||||
return self.values() == other
|
||||
|
||||
def __repr__(self):
|
||||
return f'<{self}>'
|
||||
|
||||
+28
-5
@@ -180,7 +180,7 @@ class DictModel(BaseModel):
|
||||
|
||||
|
||||
def test_dict_values():
|
||||
assert DictModel(v={'foo': 1}).values == {'v': {'foo': 1}}
|
||||
assert DictModel(v={'foo': 1}).values() == {'v': {'foo': 1}}
|
||||
|
||||
|
||||
@pytest.mark.parametrize('value,result', [
|
||||
@@ -269,7 +269,7 @@ def test_recursive_list():
|
||||
assert "<MasterListModel v=[<SubModel name='testing' count=4>]>" == repr(m)
|
||||
assert m.v[0].name == 'testing'
|
||||
assert m.v[0].count == 4
|
||||
assert m.values == {'v': [{'count': 4, 'name': 'testing'}]}
|
||||
assert m.values() == {'v': [{'count': 4, 'name': 'testing'}]}
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
MasterListModel(v=['x'])
|
||||
@@ -356,9 +356,9 @@ def test_str_enum():
|
||||
def test_any_dict():
|
||||
class Model(BaseModel):
|
||||
v: Dict[int, Any] = ...
|
||||
assert Model(v={1: 'foobar'}).values == {'v': {1: 'foobar'}}
|
||||
assert Model(v={123: 456}).values == {'v': {123: 456}}
|
||||
assert Model(v={2: [1, 2, 3]}).values == {'v': {2: [1, 2, 3]}}
|
||||
assert Model(v={1: 'foobar'}).values() == {'v': {1: 'foobar'}}
|
||||
assert Model(v={123: 456}).values() == {'v': {123: 456}}
|
||||
assert Model(v={2: [1, 2, 3]}).values() == {'v': {2: [1, 2, 3]}}
|
||||
|
||||
|
||||
def test_infer_alias():
|
||||
@@ -402,3 +402,26 @@ def test_annotation_config():
|
||||
assert list(Model.__fields__.keys()) == ['b', 'a']
|
||||
assert [f.alias for f in Model.__fields__.values()] == ['b', 'foobar']
|
||||
assert Model(foobar='123').a == 123.0
|
||||
|
||||
|
||||
def test_success_values_include():
|
||||
class Model(BaseModel):
|
||||
a: int = 1
|
||||
b: int = 2
|
||||
c: int = 3
|
||||
|
||||
m = Model()
|
||||
assert m.values() == {'a': 1, 'b': 2, 'c': 3}
|
||||
assert m.values(include={'a'}) == {'a': 1}
|
||||
assert m.values(exclude={'a'}) == {'b': 2, 'c': 3}
|
||||
assert m.values(include={'a', 'b'}, exclude={'a'}) == {'b': 2}
|
||||
|
||||
|
||||
def test_values_order():
|
||||
class Model(BaseModel):
|
||||
a: int = 1
|
||||
b: int = 2
|
||||
c: int = 3
|
||||
|
||||
m = Model(c=30, b=20, a=10)
|
||||
assert list(m) == [('a', 10), ('b', 20), ('c', 30)]
|
||||
|
||||
+7
-7
@@ -244,19 +244,19 @@ def test_allow_extra():
|
||||
class Config:
|
||||
allow_extra = True
|
||||
|
||||
assert Model(a='10.2', b=12).values == {'a': 10.2, 'b': 12}
|
||||
assert Model(a='10.2', b=12).values() == {'a': 10.2, 'b': 12}
|
||||
|
||||
|
||||
def test_set_attr():
|
||||
m = UltraSimpleModel(a=10.2)
|
||||
assert m.values == {'a': 10.2, 'b': 10}
|
||||
assert m.values() == {'a': 10.2, 'b': 10}
|
||||
m.setattr('b', 20)
|
||||
assert m.values == {'a': 10.2, 'b': 20}
|
||||
assert m.values() == {'a': 10.2, 'b': 20}
|
||||
|
||||
|
||||
def test_set_attr_invalid():
|
||||
m = UltraSimpleModel(a=10.2)
|
||||
assert m.values == {'a': 10.2, 'b': 10}
|
||||
assert m.values() == {'a': 10.2, 'b': 10}
|
||||
with pytest.raises(ValueError) as exc_info:
|
||||
m.setattr('c', 20)
|
||||
assert '"UltraSimpleModel" object has no field "c"' in str(exc_info)
|
||||
@@ -280,9 +280,9 @@ def test_alias():
|
||||
}
|
||||
|
||||
assert Model().a == 'foobar'
|
||||
assert Model().values == {'a': 'foobar'}
|
||||
assert Model().values() == {'a': 'foobar'}
|
||||
assert Model(_a='different').a == 'different'
|
||||
assert Model(_a='different').values == {'a': 'different'}
|
||||
assert Model(_a='different').values() == {'a': 'different'}
|
||||
|
||||
|
||||
def test_field_order():
|
||||
@@ -303,7 +303,7 @@ def test_required():
|
||||
b: int = 10
|
||||
|
||||
m = Model(a=10.2)
|
||||
assert m.values == dict(a=10.2, b=10)
|
||||
assert m.values() == dict(a=10.2, b=10)
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
Model()
|
||||
|
||||
+3
-3
@@ -148,7 +148,7 @@ def test_default_validators(field, value, result):
|
||||
with pytest.raises(ValidationError):
|
||||
CheckModel(**kwargs)
|
||||
else:
|
||||
assert CheckModel(**kwargs).values[field] == result
|
||||
assert CheckModel(**kwargs).values()[field] == result
|
||||
|
||||
|
||||
class DatetimeModel(BaseModel):
|
||||
@@ -348,7 +348,7 @@ def test_tuple():
|
||||
m = ListDictTupleModel(d=(1, 2, '3'))
|
||||
assert m.a is None
|
||||
assert m.d == (1, 2, '3')
|
||||
assert m.values == {'a': None, 'b': None, 'c': None, 'd': (1, 2, '3')}
|
||||
assert m.values() == {'a': None, 'b': None, 'c': None, 'd': (1, 2, '3')}
|
||||
assert ListDictTupleModel(d='xyz').d == ('x', 'y', 'z')
|
||||
assert ListDictTupleModel(d=(i**2 for i in range(5))).d == (0, 1, 4, 9, 16)
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
@@ -376,5 +376,5 @@ def test_set():
|
||||
|
||||
m = SetModel(v=[1, 2, 3])
|
||||
assert m.v == {1, 2, 3}
|
||||
assert m.values == {'v': {1, 2, 3}}
|
||||
assert m.values() == {'v': {1, 2, 3}}
|
||||
assert SetModel(v={'a', 'b', 'c'}).v == {'a', 'b', 'c'}
|
||||
|
||||
Reference in New Issue
Block a user