mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 17:47:04 +03:00
Added allow_empty flag for ListField, ListSerializer, ManyRelation, MultipleChoiceField.
This commit is contained in:
parent
81709a2c73
commit
264d423493
|
@ -1141,10 +1141,15 @@ class ChoiceField(Field):
|
||||||
class MultipleChoiceField(ChoiceField):
|
class MultipleChoiceField(ChoiceField):
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid_choice': _('"{input}" is not a valid choice.'),
|
'invalid_choice': _('"{input}" is not a valid choice.'),
|
||||||
'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 selection may not be empty.')
|
||||||
}
|
}
|
||||||
default_empty_html = []
|
default_empty_html = []
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.allow_empty = kwargs.pop('allow_empty', True)
|
||||||
|
super(MultipleChoiceField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def get_value(self, dictionary):
|
def get_value(self, dictionary):
|
||||||
# We override the default field access in order to support
|
# We override the default field access in order to support
|
||||||
# lists in HTML forms.
|
# lists in HTML forms.
|
||||||
|
@ -1159,6 +1164,8 @@ class MultipleChoiceField(ChoiceField):
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
if isinstance(data, type('')) or not hasattr(data, '__iter__'):
|
if isinstance(data, type('')) or not hasattr(data, '__iter__'):
|
||||||
self.fail('not_a_list', input_type=type(data).__name__)
|
self.fail('not_a_list', input_type=type(data).__name__)
|
||||||
|
if not self.allow_empty and len(data) == 0:
|
||||||
|
self.fail('empty')
|
||||||
|
|
||||||
return set([
|
return set([
|
||||||
super(MultipleChoiceField, self).to_internal_value(item)
|
super(MultipleChoiceField, self).to_internal_value(item)
|
||||||
|
@ -1263,11 +1270,13 @@ class ListField(Field):
|
||||||
child = _UnvalidatedField()
|
child = _UnvalidatedField()
|
||||||
initial = []
|
initial = []
|
||||||
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.')
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
|
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
|
||||||
super(ListField, self).__init__(*args, **kwargs)
|
super(ListField, self).__init__(*args, **kwargs)
|
||||||
self.child.bind(field_name='', parent=self)
|
self.child.bind(field_name='', parent=self)
|
||||||
|
@ -1287,6 +1296,8 @@ class ListField(Field):
|
||||||
data = html.parse_html_list(data)
|
data = html.parse_html_list(data)
|
||||||
if isinstance(data, type('')) or not hasattr(data, '__iter__'):
|
if isinstance(data, type('')) or not hasattr(data, '__iter__'):
|
||||||
self.fail('not_a_list', input_type=type(data).__name__)
|
self.fail('not_a_list', input_type=type(data).__name__)
|
||||||
|
if not self.allow_empty and len(data) == 0:
|
||||||
|
self.fail('empty')
|
||||||
return [self.child.run_validation(item) for item in data]
|
return [self.child.run_validation(item) for item in data]
|
||||||
|
|
||||||
def to_representation(self, data):
|
def to_representation(self, data):
|
||||||
|
|
|
@ -32,7 +32,7 @@ class PKOnlyObject(object):
|
||||||
# rather than the parent serializer.
|
# rather than the parent serializer.
|
||||||
MANY_RELATION_KWARGS = (
|
MANY_RELATION_KWARGS = (
|
||||||
'read_only', 'write_only', 'required', 'default', 'initial', 'source',
|
'read_only', 'write_only', 'required', 'default', 'initial', 'source',
|
||||||
'label', 'help_text', 'style', 'error_messages'
|
'label', 'help_text', 'style', 'error_messages', 'allow_empty'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -366,9 +366,14 @@ class ManyRelatedField(Field):
|
||||||
"""
|
"""
|
||||||
initial = []
|
initial = []
|
||||||
default_empty_html = []
|
default_empty_html = []
|
||||||
|
default_error_messages = {
|
||||||
|
'not_a_list': _('Expected a list of items but got type "{input_type}".'),
|
||||||
|
'empty': _('This list may not be empty.')
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, child_relation=None, *args, **kwargs):
|
def __init__(self, child_relation=None, *args, **kwargs):
|
||||||
self.child_relation = child_relation
|
self.child_relation = child_relation
|
||||||
|
self.allow_empty = kwargs.pop('allow_empty', True)
|
||||||
assert child_relation is not None, '`child_relation` is a required argument.'
|
assert child_relation is not None, '`child_relation` is a required argument.'
|
||||||
super(ManyRelatedField, self).__init__(*args, **kwargs)
|
super(ManyRelatedField, self).__init__(*args, **kwargs)
|
||||||
self.child_relation.bind(field_name='', parent=self)
|
self.child_relation.bind(field_name='', parent=self)
|
||||||
|
@ -386,6 +391,11 @@ class ManyRelatedField(Field):
|
||||||
return dictionary.get(self.field_name, empty)
|
return dictionary.get(self.field_name, empty)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
|
if isinstance(data, type('')) or not hasattr(data, '__iter__'):
|
||||||
|
self.fail('not_a_list', input_type=type(data).__name__)
|
||||||
|
if not self.allow_empty and len(data) == 0:
|
||||||
|
self.fail('empty')
|
||||||
|
|
||||||
return [
|
return [
|
||||||
self.child_relation.to_internal_value(item)
|
self.child_relation.to_internal_value(item)
|
||||||
for item in data
|
for item in data
|
||||||
|
|
|
@ -49,7 +49,7 @@ from rest_framework.relations import * # NOQA # isort:skip
|
||||||
# rather than the parent serializer.
|
# rather than the parent serializer.
|
||||||
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',
|
'label', 'help_text', 'style', 'error_messages', 'allow_empty',
|
||||||
'instance', 'data', 'partial', 'context'
|
'instance', 'data', 'partial', 'context'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -493,11 +493,13 @@ class ListSerializer(BaseSerializer):
|
||||||
many = True
|
many = True
|
||||||
|
|
||||||
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.')
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
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(ListSerializer, self).__init__(*args, **kwargs)
|
super(ListSerializer, self).__init__(*args, **kwargs)
|
||||||
|
@ -553,6 +555,12 @@ class ListSerializer(BaseSerializer):
|
||||||
api_settings.NON_FIELD_ERRORS_KEY: [message]
|
api_settings.NON_FIELD_ERRORS_KEY: [message]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if not self.allow_empty and len(data) == 0:
|
||||||
|
message = self.error_messages['empty']
|
||||||
|
raise ValidationError({
|
||||||
|
api_settings.NON_FIELD_ERRORS_KEY: [message]
|
||||||
|
})
|
||||||
|
|
||||||
ret = []
|
ret = []
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
|
|
|
@ -1140,6 +1140,27 @@ class TestMultipleChoiceField(FieldValues):
|
||||||
assert field.get_value(QueryDict({})) == rest_framework.fields.empty
|
assert field.get_value(QueryDict({})) == rest_framework.fields.empty
|
||||||
|
|
||||||
|
|
||||||
|
class TestEmptyMultipleChoiceField(FieldValues):
|
||||||
|
"""
|
||||||
|
Invalid values for `MultipleChoiceField(allow_empty=False)`.
|
||||||
|
"""
|
||||||
|
valid_inputs = {
|
||||||
|
}
|
||||||
|
invalid_inputs = (
|
||||||
|
([], ['This selection may not be empty.']),
|
||||||
|
)
|
||||||
|
outputs = [
|
||||||
|
]
|
||||||
|
field = serializers.MultipleChoiceField(
|
||||||
|
choices=[
|
||||||
|
('consistency', 'Consistency'),
|
||||||
|
('availability', 'Availability'),
|
||||||
|
('partition', 'Partition tolerance'),
|
||||||
|
],
|
||||||
|
allow_empty=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# File serializers...
|
# File serializers...
|
||||||
|
|
||||||
class MockFile:
|
class MockFile:
|
||||||
|
@ -1233,7 +1254,8 @@ class TestListField(FieldValues):
|
||||||
"""
|
"""
|
||||||
valid_inputs = [
|
valid_inputs = [
|
||||||
([1, 2, 3], [1, 2, 3]),
|
([1, 2, 3], [1, 2, 3]),
|
||||||
(['1', '2', '3'], [1, 2, 3])
|
(['1', '2', '3'], [1, 2, 3]),
|
||||||
|
([], [])
|
||||||
]
|
]
|
||||||
invalid_inputs = [
|
invalid_inputs = [
|
||||||
('not a list', ['Expected a list of items but got type "str".']),
|
('not a list', ['Expected a list of items but got type "str".']),
|
||||||
|
@ -1246,6 +1268,18 @@ class TestListField(FieldValues):
|
||||||
field = serializers.ListField(child=serializers.IntegerField())
|
field = serializers.ListField(child=serializers.IntegerField())
|
||||||
|
|
||||||
|
|
||||||
|
class TestEmptyListField(FieldValues):
|
||||||
|
"""
|
||||||
|
Values for `ListField` with allow_empty=False flag.
|
||||||
|
"""
|
||||||
|
valid_inputs = {}
|
||||||
|
invalid_inputs = [
|
||||||
|
([], ['This list may not be empty.'])
|
||||||
|
]
|
||||||
|
outputs = {}
|
||||||
|
field = serializers.ListField(child=serializers.IntegerField(), allow_empty=False)
|
||||||
|
|
||||||
|
|
||||||
class TestUnvalidatedListField(FieldValues):
|
class TestUnvalidatedListField(FieldValues):
|
||||||
"""
|
"""
|
||||||
Values for `ListField` with no `child` argument.
|
Values for `ListField` with no `child` argument.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user