mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-10 19:56:59 +03:00
Add max_length and min_length options to ListSerializer (#8165)
This commit is contained in:
parent
761f56ef40
commit
f0a5b958a1
|
@ -755,6 +755,14 @@ The following argument can also be passed to a `ListSerializer` field or a seria
|
|||
|
||||
This is `True` by default, but can be set to `False` if you want to disallow empty lists as valid input.
|
||||
|
||||
### `max_length`
|
||||
|
||||
This is `None` by default, but can be set to a positive integer if you want to validates that the list contains no more than this number of elements.
|
||||
|
||||
### `min_length`
|
||||
|
||||
This is `None` by default, but can be set to a positive integer if you want to validates that the list contains no fewer than this number of elements.
|
||||
|
||||
### Customizing `ListSerializer` behavior
|
||||
|
||||
There *are* a few use cases when you might want to customize the `ListSerializer` behavior. For example:
|
||||
|
|
|
@ -71,7 +71,8 @@ from rest_framework.relations import Hyperlink, PKOnlyObject # NOQA # isort:ski
|
|||
LIST_SERIALIZER_KWARGS = (
|
||||
'read_only', 'write_only', 'required', 'default', 'initial', 'source',
|
||||
'label', 'help_text', 'style', 'error_messages', 'allow_empty',
|
||||
'instance', 'data', 'partial', 'context', 'allow_null'
|
||||
'instance', 'data', 'partial', 'context', 'allow_null',
|
||||
'max_length', 'min_length'
|
||||
)
|
||||
|
||||
ALL_FIELDS = '__all__'
|
||||
|
@ -143,12 +144,18 @@ class BaseSerializer(Field):
|
|||
return CustomListSerializer(*args, **kwargs)
|
||||
"""
|
||||
allow_empty = kwargs.pop('allow_empty', None)
|
||||
max_length = kwargs.pop('max_length', None)
|
||||
min_length = kwargs.pop('min_length', None)
|
||||
child_serializer = cls(*args, **kwargs)
|
||||
list_kwargs = {
|
||||
'child': child_serializer,
|
||||
}
|
||||
if allow_empty is not None:
|
||||
list_kwargs['allow_empty'] = allow_empty
|
||||
if max_length is not None:
|
||||
list_kwargs['max_length'] = max_length
|
||||
if min_length is not None:
|
||||
list_kwargs['min_length'] = min_length
|
||||
list_kwargs.update({
|
||||
key: value for key, value in kwargs.items()
|
||||
if key in LIST_SERIALIZER_KWARGS
|
||||
|
@ -568,12 +575,16 @@ class ListSerializer(BaseSerializer):
|
|||
|
||||
default_error_messages = {
|
||||
'not_a_list': _('Expected a list of items but got type "{input_type}".'),
|
||||
'empty': _('This list may not be empty.')
|
||||
'empty': _('This list may not be empty.'),
|
||||
'max_length': _('Ensure this field has no more than {max_length} elements.'),
|
||||
'min_length': _('Ensure this field has at least {min_length} elements.')
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.child = kwargs.pop('child', copy.deepcopy(self.child))
|
||||
self.allow_empty = kwargs.pop('allow_empty', True)
|
||||
self.max_length = kwargs.pop('max_length', None)
|
||||
self.min_length = kwargs.pop('min_length', None)
|
||||
assert self.child is not None, '`child` is a required argument.'
|
||||
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -635,6 +646,18 @@ class ListSerializer(BaseSerializer):
|
|||
api_settings.NON_FIELD_ERRORS_KEY: [message]
|
||||
}, code='empty')
|
||||
|
||||
if self.max_length is not None and len(data) > self.max_length:
|
||||
message = self.error_messages['max_length'].format(max_length=self.max_length)
|
||||
raise ValidationError({
|
||||
api_settings.NON_FIELD_ERRORS_KEY: [message]
|
||||
}, code='max_length')
|
||||
|
||||
if self.min_length is not None and len(data) < self.min_length:
|
||||
message = self.error_messages['min_length'].format(min_length=self.min_length)
|
||||
raise ValidationError({
|
||||
api_settings.NON_FIELD_ERRORS_KEY: [message]
|
||||
}, code='min_length')
|
||||
|
||||
ret = []
|
||||
errors = []
|
||||
|
||||
|
|
|
@ -616,3 +616,70 @@ class TestEmptyListSerializer:
|
|||
|
||||
assert serializer.is_valid()
|
||||
assert serializer.validated_data == []
|
||||
|
||||
|
||||
class TestMaxMinLengthListSerializer:
|
||||
"""
|
||||
Tests the behaviour of ListSerializers when max_length and min_length are used
|
||||
"""
|
||||
|
||||
def setup(self):
|
||||
class IntegerSerializer(serializers.Serializer):
|
||||
some_int = serializers.IntegerField()
|
||||
|
||||
class MaxLengthSerializer(serializers.Serializer):
|
||||
many_int = IntegerSerializer(many=True, max_length=5)
|
||||
|
||||
class MinLengthSerializer(serializers.Serializer):
|
||||
many_int = IntegerSerializer(many=True, min_length=3)
|
||||
|
||||
class MaxMinLengthSerializer(serializers.Serializer):
|
||||
many_int = IntegerSerializer(many=True, min_length=3, max_length=5)
|
||||
|
||||
self.MaxLengthSerializer = MaxLengthSerializer
|
||||
self.MinLengthSerializer = MinLengthSerializer
|
||||
self.MaxMinLengthSerializer = MaxMinLengthSerializer
|
||||
|
||||
def test_min_max_length_two_items(self):
|
||||
input_data = {'many_int': [{'some_int': i} for i in range(2)]}
|
||||
|
||||
max_serializer = self.MaxLengthSerializer(data=input_data)
|
||||
min_serializer = self.MinLengthSerializer(data=input_data)
|
||||
max_min_serializer = self.MaxMinLengthSerializer(data=input_data)
|
||||
|
||||
assert max_serializer.is_valid()
|
||||
assert max_serializer.validated_data == input_data
|
||||
|
||||
assert not min_serializer.is_valid()
|
||||
|
||||
assert not max_min_serializer.is_valid()
|
||||
|
||||
def test_min_max_length_four_items(self):
|
||||
input_data = {'many_int': [{'some_int': i} for i in range(4)]}
|
||||
|
||||
max_serializer = self.MaxLengthSerializer(data=input_data)
|
||||
min_serializer = self.MinLengthSerializer(data=input_data)
|
||||
max_min_serializer = self.MaxMinLengthSerializer(data=input_data)
|
||||
|
||||
assert max_serializer.is_valid()
|
||||
assert max_serializer.validated_data == input_data
|
||||
|
||||
assert min_serializer.is_valid()
|
||||
assert min_serializer.validated_data == input_data
|
||||
|
||||
assert max_min_serializer.is_valid()
|
||||
assert min_serializer.validated_data == input_data
|
||||
|
||||
def test_min_max_length_six_items(self):
|
||||
input_data = {'many_int': [{'some_int': i} for i in range(6)]}
|
||||
|
||||
max_serializer = self.MaxLengthSerializer(data=input_data)
|
||||
min_serializer = self.MinLengthSerializer(data=input_data)
|
||||
max_min_serializer = self.MaxMinLengthSerializer(data=input_data)
|
||||
|
||||
assert not max_serializer.is_valid()
|
||||
|
||||
assert min_serializer.is_valid()
|
||||
assert min_serializer.validated_data == input_data
|
||||
|
||||
assert not max_min_serializer.is_valid()
|
||||
|
|
Loading…
Reference in New Issue
Block a user