Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,4 @@ Contributors (chronological)
- Nadège Michel `@nadege <https://github.com/nadege>`_
- Tamara `@infinityxxx <https://github.com/infinityxxx>`_
- Stephen Rosen `@sirosen <https://github.com/sirosen>`_
- Ficapy `@ficapy <https://github.com/ficapy>`_
2 changes: 2 additions & 0 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ It is sometimes convenient to write validators as methods. Use the `validates <m
class ItemSchema(Schema):
quantity = fields.Integer()

# You can use the order parameter to allow validations to be executed first
# If the order value is large, execute first, and the default value of order is 0
@validates("quantity")
def validate_quantity(self, value):
if value < 0:
Expand Down
2 changes: 1 addition & 1 deletion src/marshmallow/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from marshmallow.exceptions import ValidationError
from distutils.version import LooseVersion

__version__ = "3.10.0"
__version__ = "3.10.1"
__version_info__ = tuple(LooseVersion(__version__).version)
__all__ = [
"EXCLUDE",
Expand Down
9 changes: 7 additions & 2 deletions src/marshmallow/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,17 @@ def validate_age(self, data, **kwargs):
VALIDATES_SCHEMA = "validates_schema"


def validates(field_name: str):
def validates(field_name: str, order: int = 0):
"""Register a field validator.

:param str field_name: Name of the field that the method validates.
:param int order: In the same schema,the larger value is executed first.

.. warning::

The order parameter cannot be used across schema
"""
return set_hook(None, VALIDATES, field_name=field_name)
return set_hook(None, VALIDATES, field_name=field_name, order=order)


def validates_schema(
Expand Down
17 changes: 13 additions & 4 deletions src/marshmallow/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def resolve_hooks(cls) -> typing.Dict[types.Tag, typing.List[str]]:
"""
mro = inspect.getmro(cls)

hooks = defaultdict(list) # type: typing.Dict[types.Tag, typing.List[str]]
hooks = defaultdict(list) # type: typing.Dict[types.Tag, typing.Any]

for attr_name in dir(cls):
# Need to look up the actual descriptor, not whatever might be
Expand All @@ -185,12 +185,21 @@ def resolve_hooks(cls) -> typing.Dict[types.Tag, typing.List[str]]:
except AttributeError:
pass
else:
for key in hook_config.keys():
for key, value in hook_config.items():
# Use name here so we can get the bound method later, in
# case the processor was a descriptor or something.
hooks[key].append(attr_name)
hooks[key].append(
{"order": value.get("order", 0), "attr_name": attr_name}
)

return hooks
ordered_hooks = {
k: [
i["attr_name"]
for i in sorted(v, key=lambda x: x["order"], reverse=True)
]
for k, v in hooks.items()
}
return defaultdict(list, ordered_hooks)


class SchemaOpts:
Expand Down
27 changes: 27 additions & 0 deletions tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -2912,3 +2912,30 @@ class DefinitelyUniqueSchema(Schema):

SchemaClass = class_registry.get_class(DefinitelyUniqueSchema.__name__)
assert SchemaClass is DefinitelyUniqueSchema


def test_validates_order():
class User(Schema):
name = fields.Str()
age = fields.Int()
sex = fields.Int()

@validates("name", order=1)
def validate_name(self, value):
self.context["order"] += "1"
return value

@validates("age", order=2)
def validate_age(self, value):
self.context["order"] += "2"
return value

@validates("sex")
def validate_sex(self, value):
self.context["order"] += "0"
return value

schema = User()
schema.context["order"] = ""
schema.load({"name": "name", "age": 24, "sex": 1})
assert schema.context["order"] == "210"