例如,我要检查数据是否包含格式正确的字典列表,并且此列表的长度在1到10之间。
from marshmallow import Schema, fields
class Record(Schema):
id = fields.Integer(required=True)
# more fields here, let's omit them
schema = Record(many=True)
# somehow define that we have constraint on list length
# list length should be between 1 and 10 inclusive
# validation should fail
errors = schema.validate([])
assert errors # length < 1
errors = schema.validate([{"id": i} for i in range(100)])
assert errors # length > 10
# validation should succeed
errors = schema.validate([{"id": i} for i in range(5)])
assert not errors
是否可以使用棉花糖定义此类约束?
我需要这样的东西,但我想避免在数据中嵌套更多级别:
from marshmallow.validate import Length
class BatchOfRecords(Schema):
records = fields.Nested(
Record,
required=True,
many=True,
validate=Length(1, 10)
)
UPD:
因此,为了澄清这个问题,我想验证一列字典:
[
{"id": 1},
{"id": 2},
...
]
不是字典,其键包含字典列表:
# it works but it introduces extra level of nesting,
# I want to avoid it
{
"records": [
{"id": 1},
{"id": 2},
...
]
}
答案 0 :(得分:1)
编辑
因此可以仅使用棉花糖来验证集合。您可以将pass_many
kwarg与pre_load
或post_load
方法一起使用。我在pre_load
上没有获得成功,但是开始从事邮政工作。 pass_many
kwarg会将输入视为集合,因此您可以在加载后检查集合的长度。我使用many
kwarg,以便仅在传递记录集而不是单个记录时才检查长度
from marshmallow import Schema, fields, ValidationError, post_load
class Record(Schema):
id = fields.Integer(required=True)
name = fields.String(required=True)
status = fields.String(required=True)
@post_load(pass_many=True)
def check_length(self, data, many, **kwargs):
if many:
if len(data) < 1 or len(data) > 10:
raise ValidationError(message=['Record length should be greater than 1 and less than 10.'],
field_name='record')
编辑测试案例
from unittest import TestCase
from marshmallow import ValidationError
from stack_marshmallow import Record
class TestStackSchemasNonNested(TestCase):
def test_empty_dict(self):
with self.assertRaises(ValidationError) as exc:
Record(many=True).load([])
self.assertEqual(exc.exception.messages['record'], ['Record length should be greater than 1 and less than 10.'])
def test_happy_path(self):
user_data = [{"id": "1", "name": "apple", "status": "OK"}, {"id": "2", "name": "apple", "status": 'OK'}]
data = Record(many=True).load(user_data)
self.assertEqual(len(data), 2)
def test_invalid_values_with_valid_values(self):
user_data = [{"id": "1", "name": "apple", "status": 'OK'}, {"id": "2"}]
with self.assertRaises(ValidationError) as exc:
Record(many=True).load(user_data)
self.assertEqual(exc.exception.messages[1]['name'], ['Missing data for required field.'])
self.assertEqual(exc.exception.messages[1]['status'], ['Missing data for required field.'])
def test_too_many(self):
user_data = [{"id": "1", "name": "apple", "status": "OK"},
{"id": "2", "name": "apple", "status": 'OK'},
{"id": "3", "name": "apple", "status": 'OK'},
{"id": "4", "name": "apple", "status": 'OK'},
{"id": "5", "name": "apple", "status": 'OK'},
{"id": "6", "name": "apple", "status": 'OK'},
{"id": "7", "name": "apple", "status": 'OK'},
{"id": "8", "name": "apple", "status": 'OK'},
{"id": "9", "name": "apple", "status": 'OK'},
{"id": "10", "name": "apple", "status": 'OK'},
{"id": "11", "name": "apple", "status": 'OK'},
]
with self.assertRaises(ValidationError) as exc:
Record(many=True).load(user_data)
self.assertEqual(exc.exception.messages['record'], ['Record length should be greater than 1 and less than 10.'])
编辑源:https://marshmallow.readthedocs.io/en/stable/extending.html
您非常亲密。我增加了一些记录的复杂性,因为我认为您不会只拥有一个字段,否则我只会使用整数列表。我还添加了一些单元测试,以便您了解如何进行测试。
from marshmallow import Schema, fields, validate
class Record(Schema):
id = fields.Integer(required=True)
name = fields.String(required=True)
status = fields.String(required=True)
class Records(Schema):
records = fields.List(
fields.Nested(Record),
required=True,
validate=validate.Length(min=1,max=10)
)
测试案例
from unittest import TestCase
from marshmallow import ValidationError
from stack_marshmallow import Records
class TestStackSchemas(TestCase):
def setUp(self):
self.schema = Records()
def test_empty_dict(self):
with self.assertRaises(ValidationError) as exc:
self.schema.load({})
self.assertEqual(exc.exception.messages['records'], ['Missing data for required field.'])
def test_empty_empty_list_in_dict(self):
with self.assertRaises(ValidationError) as exc:
self.schema.load({"records": []})
self.assertEqual(exc.exception.messages['records'], ['Length must be between 1 and 10.'])
def test_missing_fields_in_single_record(self):
with self.assertRaises(ValidationError) as exc:
self.schema.load({"records": [{"id": 1}]})
self.assertEqual(exc.exception.messages['records'][0]['name'], ['Missing data for required field.'])
self.assertEqual(exc.exception.messages['records'][0]['status'], ['Missing data for required field.'])
def test_list_too_long_and_invalid_records(self):
with self.assertRaises(ValidationError) as exc:
self.schema.load({"records":
[{"id": 1, "name": "stack", "status": "overflow"},
{"id": 2, "name": "stack", "status": "overflow"},
{"id": 3, "name": "stack", "status": "overflow"},
{"id": 4, "name": "stack", "status": "overflow"},
{"id": 5, "name": "stack", "status": "overflow"},
{"id": 6, "name": "stack", "status": "overflow"},
{"id": 7, "name": "stack", "status": "overflow"},
{"id": 8, "name": "stack", "status": "overflow"},
{"id": 9, "name": "stack", "status": "overflow"},
{"id": 10, "name": "stack", "status": "overflow"},
{"id": 11, "name": "stack", "status": "overflow"}]})
self.assertEqual(exc.exception.messages['records'], ['Length must be between 1 and 10.'])
来源:https://marshmallow.readthedocs.io/en/stable/nesting.html和 https://marshmallow.readthedocs.io/en/stable/examples.html
答案 1 :(得分:0)
我想做的事可以使用这个小库:https://github.com/and-semakin/marshmallow-toplevel。
pip install marshmallow-toplevel
用法:
from marshmallow.validate import Length
from marshmallow_toplevel import TopLevelSchema
class BatchOfRecords(TopLevelSchema):
_toplevel = fields.Nested(
Record,
required=True,
many=True,
validate=Length(1, 10)
)