mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-14 05:36:50 +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.
|
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
|
### Customizing `ListSerializer` behavior
|
||||||
|
|
||||||
There *are* a few use cases when you might want to customize the `ListSerializer` behavior. For example:
|
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 = (
|
LIST_SERIALIZER_KWARGS = (
|
||||||
'read_only', 'write_only', 'required', 'default', 'initial', 'source',
|
'read_only', 'write_only', 'required', 'default', 'initial', 'source',
|
||||||
'label', 'help_text', 'style', 'error_messages', 'allow_empty',
|
'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__'
|
ALL_FIELDS = '__all__'
|
||||||
|
@ -143,12 +144,18 @@ class BaseSerializer(Field):
|
||||||
return CustomListSerializer(*args, **kwargs)
|
return CustomListSerializer(*args, **kwargs)
|
||||||
"""
|
"""
|
||||||
allow_empty = kwargs.pop('allow_empty', None)
|
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)
|
child_serializer = cls(*args, **kwargs)
|
||||||
list_kwargs = {
|
list_kwargs = {
|
||||||
'child': child_serializer,
|
'child': child_serializer,
|
||||||
}
|
}
|
||||||
if allow_empty is not None:
|
if allow_empty is not None:
|
||||||
list_kwargs['allow_empty'] = allow_empty
|
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({
|
list_kwargs.update({
|
||||||
key: value for key, value in kwargs.items()
|
key: value for key, value in kwargs.items()
|
||||||
if key in LIST_SERIALIZER_KWARGS
|
if key in LIST_SERIALIZER_KWARGS
|
||||||
|
@ -568,12 +575,16 @@ class ListSerializer(BaseSerializer):
|
||||||
|
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'not_a_list': _('Expected a list of items but got type "{input_type}".'),
|
'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):
|
def __init__(self, *args, **kwargs):
|
||||||
self.child = kwargs.pop('child', copy.deepcopy(self.child))
|
self.child = kwargs.pop('child', copy.deepcopy(self.child))
|
||||||
self.allow_empty = kwargs.pop('allow_empty', True)
|
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 self.child is not None, '`child` is a required argument.'
|
||||||
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
|
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
@ -635,6 +646,18 @@ class ListSerializer(BaseSerializer):
|
||||||
api_settings.NON_FIELD_ERRORS_KEY: [message]
|
api_settings.NON_FIELD_ERRORS_KEY: [message]
|
||||||
}, code='empty')
|
}, 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 = []
|
ret = []
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
|
|
|
@ -616,3 +616,70 @@ class TestEmptyListSerializer:
|
||||||
|
|
||||||
assert serializer.is_valid()
|
assert serializer.is_valid()
|
||||||
assert serializer.validated_data == []
|
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