aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework
diff options
context:
space:
mode:
Diffstat (limited to 'rest_framework')
-rw-r--r--rest_framework/fields.py21
-rw-r--r--rest_framework/serializers.py3
-rw-r--r--rest_framework/utils/field_mapping.py58
3 files changed, 44 insertions, 38 deletions
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index 48a3e1ab..f5bae734 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -102,6 +102,7 @@ class Field(object):
'null': _('This field may not be null.')
}
default_validators = []
+ default_empty_html = None
def __init__(self, read_only=False, write_only=False,
required=None, default=empty, initial=None, source=None,
@@ -185,6 +186,11 @@ class Field(object):
Given the *incoming* primative data, return the value for this field
that should be validated and transformed to a native value.
"""
+ if html.is_html_input(dictionary):
+ # HTML forms will represent empty fields as '', and cannot
+ # represent None or False values directly.
+ ret = dictionary.get(self.field_name, '')
+ return self.default_empty_html if (ret == '') else ret
return dictionary.get(self.field_name, empty)
def get_attribute(self, instance):
@@ -236,9 +242,6 @@ class Field(object):
Test the given value against all the validators on the field,
and either raise a `ValidationError` or simply return.
"""
- if value in (None, '', [], (), {}):
- return
-
errors = []
for validator in self.validators:
try:
@@ -282,16 +285,10 @@ class BooleanField(Field):
default_error_messages = {
'invalid': _('`{input}` is not a valid boolean.')
}
+ default_empty_html = False
TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True))
FALSE_VALUES = set(('f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False))
- def get_value(self, dictionary):
- if html.is_html_input(dictionary):
- # HTML forms do not send a `False` value on an empty checkbox,
- # so we override the default empty value to be False.
- return dictionary.get(self.field_name, False)
- return dictionary.get(self.field_name, empty)
-
def to_internal_value(self, data):
if data in self.TRUE_VALUES:
return True
@@ -315,6 +312,7 @@ class CharField(Field):
default_error_messages = {
'blank': _('This field may not be blank.')
}
+ default_empty_html = ''
def __init__(self, **kwargs):
self.allow_blank = kwargs.pop('allow_blank', False)
@@ -323,6 +321,9 @@ class CharField(Field):
super(CharField, self).__init__(**kwargs)
def run_validation(self, data=empty):
+ # Test for the empty string here so that it does not get validated,
+ # and so that subclasses do not need to handle it explicitly
+ # inside the `to_internal_value()` method.
if data == '':
if not self.allow_blank:
self.fail('blank')
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index d9f9c8cb..949f5915 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -411,6 +411,9 @@ class ModelSerializer(Serializer):
# `ModelField`, which is used when no other typed field
# matched to the model field.
kwargs.pop('model_field', None)
+ if not issubclass(field_cls, CharField):
+ # `allow_blank` is only valid for textual fields.
+ kwargs.pop('allow_blank', None)
elif field_name in info.relations:
# Create forward and reverse relationships.
diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py
index be72e444..1c718ccb 100644
--- a/rest_framework/utils/field_mapping.py
+++ b/rest_framework/utils/field_mapping.py
@@ -49,8 +49,9 @@ def get_field_kwargs(field_name, model_field):
kwargs = {}
validator_kwarg = model_field.validators
- if model_field.null or model_field.blank:
- kwargs['required'] = False
+ # The following will only be used by ModelField classes.
+ # Gets removed for everything else.
+ kwargs['model_field'] = model_field
if model_field.verbose_name and needs_label(model_field, field_name):
kwargs['label'] = capfirst(model_field.verbose_name)
@@ -59,23 +60,26 @@ def get_field_kwargs(field_name, model_field):
kwargs['help_text'] = model_field.help_text
if isinstance(model_field, models.AutoField) or not model_field.editable:
+ # If this field is read-only, then return early.
+ # Further keyword arguments are not valid.
kwargs['read_only'] = True
- # Read only implies that the field is not required.
- # We have a cleaner repr on the instance if we don't set it.
- kwargs.pop('required', None)
+ return kwargs
if model_field.has_default():
- kwargs['default'] = model_field.get_default()
- # Having a default implies that the field is not required.
- # We have a cleaner repr on the instance if we don't set it.
- kwargs.pop('required', None)
+ kwargs['required'] = False
if model_field.flatchoices:
- # If this model field contains choices, then return now,
- # any further keyword arguments are not valid.
+ # If this model field contains choices, then return early.
+ # Further keyword arguments are not valid.
kwargs['choices'] = model_field.flatchoices
return kwargs
+ if model_field.null:
+ kwargs['allow_null'] = True
+
+ if model_field.blank:
+ kwargs['allow_blank'] = True
+
# Ensure that max_length is passed explicitly as a keyword arg,
# rather than as a validator.
max_length = getattr(model_field, 'max_length', None)
@@ -88,7 +92,10 @@ def get_field_kwargs(field_name, model_field):
# Ensure that min_length is passed explicitly as a keyword arg,
# rather than as a validator.
- min_length = getattr(model_field, 'min_length', None)
+ min_length = next((
+ validator.limit_value for validator in validator_kwarg
+ if isinstance(validator, validators.MinLengthValidator)
+ ), None)
if min_length is not None:
kwargs['min_length'] = min_length
validator_kwarg = [
@@ -153,20 +160,9 @@ def get_field_kwargs(field_name, model_field):
if decimal_places is not None:
kwargs['decimal_places'] = decimal_places
- if isinstance(model_field, models.BooleanField):
- # models.BooleanField has `blank=True`, but *is* actually
- # required *unless* a default is provided.
- # Also note that Django<1.6 uses `default=False` for
- # models.BooleanField, but Django>=1.6 uses `default=None`.
- kwargs.pop('required', None)
-
if validator_kwarg:
kwargs['validators'] = validator_kwarg
- # The following will only be used by ModelField classes.
- # Gets removed for everything else.
- kwargs['model_field'] = model_field
-
return kwargs
@@ -188,16 +184,22 @@ def get_relation_kwargs(field_name, relation_info):
kwargs.pop('queryset', None)
if model_field:
- if model_field.null or model_field.blank:
- kwargs['required'] = False
if model_field.verbose_name and needs_label(model_field, field_name):
kwargs['label'] = capfirst(model_field.verbose_name)
- if not model_field.editable:
- kwargs['read_only'] = True
- kwargs.pop('queryset', None)
help_text = clean_manytomany_helptext(model_field.help_text)
if help_text:
kwargs['help_text'] = help_text
+ if not model_field.editable:
+ kwargs['read_only'] = True
+ kwargs.pop('queryset', None)
+ if kwargs.get('read_only', False):
+ # If this field is read-only, then return early.
+ # No further keyword arguments are valid.
+ return kwargs
+ if model_field.has_default():
+ kwargs['required'] = False
+ if model_field.null:
+ kwargs['allow_null'] = True
return kwargs