diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index b527b016b..22ce7dd77 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -434,9 +434,11 @@ Requires either the `Pillow` package or `PIL` package. The `Pillow` package is A field class that validates a list of objects. -**Signature**: `ListField(child)` +**Signature**: `ListField(child, min_length=None, max_length=None)` - `child` - A field instance that should be used for validating the objects in the list. If this argument is not provided then objects in the list will not be validated. +- `min_length` - Validates that the list contains no fewer than this number of elements. +- `max_length` - Validates that the list contains no more than this number of elements. For example, to validate a list of integers you might use something like the following: diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 6d21d670a..f46edef1e 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1505,12 +1505,16 @@ class ListField(Field): initial = [] 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.'), + 'min_length': _('Ensure this field has at least {min_length} elements.'), + 'max_length': _('Ensure this field has no more than {max_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 not inspect.isclass(self.child), '`child` has not been instantiated.' assert self.child.source is None, ( @@ -1520,6 +1524,12 @@ class ListField(Field): super(ListField, self).__init__(*args, **kwargs) self.child.bind(field_name='', parent=self) + if self.max_length is not None: + message = self.error_messages['max_length'].format(max_length=self.max_length) + self.validators.append(MaxLengthValidator(self.max_length, message=message)) + if self.min_length is not None: + message = self.error_messages['min_length'].format(min_length=self.min_length) + self.validators.append(MinLengthValidator(self.min_length, message=message)) def get_value(self, dictionary): if self.field_name not in dictionary: diff --git a/tests/test_fields.py b/tests/test_fields.py index 5de6546fc..16221d4cc 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1668,6 +1668,16 @@ class TestEmptyListField(FieldValues): field = serializers.ListField(child=serializers.IntegerField(), allow_empty=False) +class TestListFieldLengthLimit(FieldValues): + valid_inputs = () + invalid_inputs = [ + ((0, 1), ['Ensure this field has at least 3 elements.']), + ((0, 1, 2, 3, 4, 5), ['Ensure this field has no more than 4 elements.']), + ] + outputs = () + field = serializers.ListField(child=serializers.IntegerField(), min_length=3, max_length=4) + + class TestUnvalidatedListField(FieldValues): """ Values for `ListField` with no `child` argument.