mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
annotation only fields first
This commit is contained in:
@@ -10,6 +10,7 @@ v0.4.0 (2017-XX-XX)
|
||||
* simplify error display
|
||||
* use unicode ellipsis in ``truncate``
|
||||
* add ``parse_obj``, ``parse_raw`` and ``parse_file`` helper functions #58
|
||||
* switch annotation only fields to come first in fields list not last
|
||||
|
||||
v0.3.0 (2017-06-21)
|
||||
...................
|
||||
|
||||
@@ -10,9 +10,9 @@ class TestPydantic:
|
||||
def __init__(self, allow_extra):
|
||||
|
||||
class Model(BaseModel):
|
||||
id: int = ...
|
||||
client_name: constr(max_length=255) = ...
|
||||
sort_index: float = ...
|
||||
id: int
|
||||
client_name: constr(max_length=255)
|
||||
sort_index: float
|
||||
# client_email: EmailStr = None
|
||||
client_phone: constr(max_length=255) = None
|
||||
|
||||
@@ -23,15 +23,15 @@ class TestPydantic:
|
||||
|
||||
contractor: PositiveInt = None
|
||||
upstream_http_referrer: constr(max_length=1023) = None
|
||||
grecaptcha_response: constr(min_length=20, max_length=1000) = ...
|
||||
grecaptcha_response: constr(min_length=20, max_length=1000)
|
||||
last_updated: datetime = None
|
||||
|
||||
class Skill(BaseModel):
|
||||
subject: str = ...
|
||||
subject_id: int = ...
|
||||
category: str = ...
|
||||
qual_level: str = ...
|
||||
qual_level_id: int = ...
|
||||
subject: str
|
||||
subject_id: int
|
||||
category: str
|
||||
qual_level: str
|
||||
qual_level_id: int
|
||||
qual_level_ranking: float = 0
|
||||
skills: List[Skill] = []
|
||||
|
||||
|
||||
+3
-3
@@ -234,7 +234,7 @@ The ellipsis notation ``...`` will not work with mypy, you need to use annotatio
|
||||
.. warning::
|
||||
|
||||
Be aware that using annotation only fields will alter the order of your fields in metadata and errors:
|
||||
annotation only fields will always come last, but still in the order they were defined.
|
||||
annotation only fields will always come first, but still in the order they were defined.
|
||||
|
||||
To get round this you can use the ``Required`` (via ``from pydantic import Required``) field as an alias for
|
||||
ellipses or annotation only.
|
||||
@@ -247,8 +247,8 @@ a model.
|
||||
|
||||
.. warning::
|
||||
|
||||
However be warned: immutability in python is never strict. If developers are determined/stupid they can always
|
||||
modify a so called "immutable" object
|
||||
Immutability in python is never strict. If developers are determined/stupid they can always
|
||||
modify a so-called "immutable" object.
|
||||
|
||||
.. literalinclude:: examples/mutation.py
|
||||
|
||||
|
||||
+17
-18
@@ -48,29 +48,18 @@ class MetaModel(type):
|
||||
fields.update(base.__fields__)
|
||||
config = inherit_config(base.config, config)
|
||||
|
||||
annotations = namespace.get('__annotations__')
|
||||
config = inherit_config(namespace.get('Config'), config)
|
||||
class_validators = {n: f for n, f in namespace.items()
|
||||
if n.startswith('validate_') and isinstance(f, FunctionType)}
|
||||
class_validators = {
|
||||
n: f for n, f in namespace.items() if n.startswith('validate_') and isinstance(f, FunctionType)
|
||||
}
|
||||
|
||||
for f in fields.values():
|
||||
f.set_config(config)
|
||||
|
||||
for var_name, value in namespace.items():
|
||||
if var_name.startswith('_') or isinstance(value, TYPE_BLACKLIST):
|
||||
continue
|
||||
fields[var_name] = Field.infer(
|
||||
name=var_name,
|
||||
value=value,
|
||||
annotation=annotations.pop(var_name, None) if annotations else None,
|
||||
class_validators=class_validators,
|
||||
config=config,
|
||||
)
|
||||
|
||||
if annotations:
|
||||
for ann_name, ann_type in annotations.items():
|
||||
if ann_name.startswith('_'):
|
||||
continue
|
||||
annotations = namespace.get('__annotations__', {})
|
||||
# annotation only fields need to come first in fields
|
||||
for ann_name, ann_type in annotations.items():
|
||||
if not ann_name.startswith('_') and ann_name not in namespace:
|
||||
fields[ann_name] = Field.infer(
|
||||
name=ann_name,
|
||||
value=...,
|
||||
@@ -79,6 +68,16 @@ class MetaModel(type):
|
||||
config=config,
|
||||
)
|
||||
|
||||
for var_name, value in namespace.items():
|
||||
if not var_name.startswith('_') and not isinstance(value, TYPE_BLACKLIST):
|
||||
fields[var_name] = Field.infer(
|
||||
name=var_name,
|
||||
value=value,
|
||||
annotation=annotations.get(var_name),
|
||||
class_validators=class_validators,
|
||||
config=config,
|
||||
)
|
||||
|
||||
new_namespace = {
|
||||
'config': config,
|
||||
'__fields__': fields,
|
||||
|
||||
@@ -397,16 +397,16 @@ _a:
|
||||
|
||||
def test_annotation_config():
|
||||
class Model(BaseModel):
|
||||
a: float
|
||||
b: int = 10
|
||||
b: float
|
||||
a: int = 10
|
||||
_c: str
|
||||
|
||||
class Config:
|
||||
fields = {'a': 'foobar'}
|
||||
fields = {'b': 'foobar'}
|
||||
|
||||
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
|
||||
assert [f.alias for f in Model.__fields__.values()] == ['foobar', 'a']
|
||||
assert Model(foobar='123').b == 123.0
|
||||
|
||||
|
||||
def test_success_values_include():
|
||||
|
||||
+1
-1
@@ -285,7 +285,7 @@ def test_field_order():
|
||||
d: dict = {}
|
||||
|
||||
# fields are ordered as defined except annotation-only fields come last
|
||||
assert list(Model.__fields__.keys()) == ['b', 'd', 'c', 'a']
|
||||
assert list(Model.__fields__.keys()) == ['c', 'a', 'b', 'd']
|
||||
|
||||
|
||||
def test_required():
|
||||
|
||||
Reference in New Issue
Block a user