aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--rest_framework/fields.py21
-rw-r--r--rest_framework/serializers.py36
-rw-r--r--rest_framework/utils/representation.py5
-rw-r--r--tests/test_fields.py4
-rw-r--r--tests/test_model_serializer.py4
-rw-r--r--tests/test_validators.py16
6 files changed, 55 insertions, 31 deletions
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index d5dccd72..8cc8e81e 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -143,7 +143,7 @@ class Field(object):
def __init__(self, read_only=False, write_only=False,
required=None, default=empty, initial=empty, source=None,
label=None, help_text=None, style=None,
- error_messages=None, validators=[], allow_null=False):
+ error_messages=None, validators=None, allow_null=False):
self._creation_counter = Field._creation_counter
Field._creation_counter += 1
@@ -166,9 +166,11 @@ class Field(object):
self.label = label
self.help_text = help_text
self.style = {} if style is None else style
- self.validators = validators[:] or self.default_validators[:]
self.allow_null = allow_null
+ if validators is not None:
+ self.validators = validators[:]
+
# These are set up by `.bind()` when the field is added to a serializer.
self.field_name = None
self.parent = None
@@ -214,6 +216,21 @@ class Field(object):
else:
self.source_attrs = self.source.split('.')
+ # .validators is a lazily loaded property, that gets its default
+ # value from `get_validators`.
+ @property
+ def validators(self):
+ if not hasattr(self, '_validators'):
+ self._validators = self.get_validators()
+ return self._validators
+
+ @validators.setter
+ def validators(self, validators):
+ self._validators = validators
+
+ def get_validators(self):
+ return self.default_validators[:]
+
def get_initial(self):
"""
Return a value to use when the field is being returned as a primitive
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index c9f70f2d..82e932dd 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -282,21 +282,23 @@ class SerializerMetaclass(type):
@six.add_metaclass(SerializerMetaclass)
class Serializer(BaseSerializer):
- def __init__(self, *args, **kwargs):
- super(Serializer, self).__init__(*args, **kwargs)
-
+ @property
+ def fields(self):
+ if not hasattr(self, '_fields'):
+ self._fields = BindingDict(self)
+ for key, value in self.get_fields().items():
+ self._fields[key] = value
+ return self._fields
+
+ def get_fields(self):
# Every new serializer is created with a clone of the field instances.
# This allows users to dynamically modify the fields on a serializer
# instance without affecting every other serializer class.
- self.fields = BindingDict(self)
- for key, value in self._get_base_fields().items():
- self.fields[key] = value
-
- self.validators = getattr(getattr(self, 'Meta', None), 'validators', [])
-
- def _get_base_fields(self):
return copy.deepcopy(self._declared_fields)
+ def get_validators(self):
+ return getattr(getattr(self, 'Meta', None), 'validators', [])
+
def get_initial(self):
if self._initial_data is not None:
return ReturnDict([
@@ -520,14 +522,6 @@ class ModelSerializer(Serializer):
})
_related_class = PrimaryKeyRelatedField
- def __init__(self, *args, **kwargs):
- super(ModelSerializer, self).__init__(*args, **kwargs)
- if 'validators' not in kwargs:
- validators = self.get_default_validators()
- if validators:
- self.validators.extend(validators)
- self._kwargs['validators'] = validators
-
def create(self, validated_attrs):
# Check that the user isn't trying to handle a writable nested field.
# If we don't do this explicitly they'd likely get a confusing
@@ -578,13 +572,13 @@ class ModelSerializer(Serializer):
instance.save()
return instance
- def get_default_validators(self):
+ def get_validators(self):
field_names = set([
field.source for field in self.fields.values()
if (field.source != '*') and ('.' not in field.source)
])
- validators = []
+ validators = getattr(getattr(self, 'Meta', None), 'validators', [])
model_class = self.Meta.model
# Note that we make sure to check `unique_together` both on the
@@ -627,7 +621,7 @@ class ModelSerializer(Serializer):
return validators
- def _get_base_fields(self):
+ def get_fields(self):
declared_fields = copy.deepcopy(self._declared_fields)
ret = SortedDict()
diff --git a/rest_framework/utils/representation.py b/rest_framework/utils/representation.py
index 180b51f8..2a7c4675 100644
--- a/rest_framework/utils/representation.py
+++ b/rest_framework/utils/representation.py
@@ -82,6 +82,11 @@ def serializer_repr(serializer, indent, force_many=None):
ret += field_repr(field.child_relation, force_many=field.child_relation)
else:
ret += field_repr(field)
+
+ if serializer.validators:
+ ret += '\n' + indent_str + 'class Meta:'
+ ret += '\n' + indent_str + ' validators = ' + smart_repr(serializer.validators)
+
return ret
diff --git a/tests/test_fields.py b/tests/test_fields.py
index 3e102ab5..96d09900 100644
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -85,7 +85,7 @@ class TestSource:
class ExampleSerializer(serializers.Serializer):
example_field = serializers.CharField(source='example_field')
with pytest.raises(AssertionError) as exc_info:
- ExampleSerializer()
+ ExampleSerializer().fields
assert str(exc_info.value) == (
"It is redundant to specify `source='example_field'` on field "
"'CharField' in serializer 'ExampleSerializer', because it is the "
@@ -1018,7 +1018,7 @@ class TestSerializerMethodField:
example_field = serializers.SerializerMethodField('get_example_field')
with pytest.raises(AssertionError) as exc_info:
- ExampleSerializer()
+ ExampleSerializer().fields
assert str(exc_info.value) == (
"It is redundant to specify `get_example_field` on "
"SerializerMethodField 'example_field' in serializer "
diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py
index 18170bc0..b8b621be 100644
--- a/tests/test_model_serializer.py
+++ b/tests/test_model_serializer.py
@@ -181,7 +181,7 @@ class TestRegularFieldMappings(TestCase):
fields = ('auto_field', 'invalid')
with self.assertRaises(ImproperlyConfigured) as excinfo:
- TestSerializer()
+ TestSerializer().fields
expected = 'Field name `invalid` is not valid for model `ModelBase`.'
assert str(excinfo.exception) == expected
@@ -198,7 +198,7 @@ class TestRegularFieldMappings(TestCase):
fields = ('auto_field',)
with self.assertRaises(ImproperlyConfigured) as excinfo:
- TestSerializer()
+ TestSerializer().fields
expected = (
'Field `missing` has been declared on serializer '
'`TestSerializer`, but is missing from `Meta.fields`.'
diff --git a/tests/test_validators.py b/tests/test_validators.py
index 6cc52c83..e6e0b23a 100644
--- a/tests/test_validators.py
+++ b/tests/test_validators.py
@@ -86,10 +86,12 @@ class TestUniquenessTogetherValidation(TestCase):
def test_repr(self):
serializer = UniquenessTogetherSerializer()
expected = dedent("""
- UniquenessTogetherSerializer(validators=[<UniqueTogetherValidator(queryset=UniquenessTogetherModel.objects.all(), fields=('race_name', 'position'))>]):
+ UniquenessTogetherSerializer():
id = IntegerField(label='ID', read_only=True)
race_name = CharField(max_length=100)
position = IntegerField()
+ class Meta:
+ validators = [<UniqueTogetherValidator(queryset=UniquenessTogetherModel.objects.all(), fields=('race_name', 'position'))>]
""")
assert repr(serializer) == expected
@@ -173,10 +175,12 @@ class TestUniquenessForDateValidation(TestCase):
def test_repr(self):
serializer = UniqueForDateSerializer()
expected = dedent("""
- UniqueForDateSerializer(validators=[<UniqueForDateValidator(queryset=UniqueForDateModel.objects.all(), field='slug', date_field='published')>]):
+ UniqueForDateSerializer():
id = IntegerField(label='ID', read_only=True)
slug = CharField(max_length=100)
published = DateField(required=True)
+ class Meta:
+ validators = [<UniqueForDateValidator(queryset=UniqueForDateModel.objects.all(), field='slug', date_field='published')>]
""")
assert repr(serializer) == expected
@@ -231,10 +235,12 @@ class TestHiddenFieldUniquenessForDateValidation(TestCase):
serializer = TestSerializer()
expected = dedent("""
- TestSerializer(validators=[<UniqueForDateValidator(queryset=HiddenFieldUniqueForDateModel.objects.all(), field='slug', date_field='published')>]):
+ TestSerializer():
id = IntegerField(label='ID', read_only=True)
slug = CharField(max_length=100)
published = HiddenField(default=CreateOnlyDefault(<function now>))
+ class Meta:
+ validators = [<UniqueForDateValidator(queryset=HiddenFieldUniqueForDateModel.objects.all(), field='slug', date_field='published')>]
""")
assert repr(serializer) == expected
@@ -246,9 +252,11 @@ class TestHiddenFieldUniquenessForDateValidation(TestCase):
serializer = TestSerializer()
expected = dedent("""
- TestSerializer(validators=[<UniqueForDateValidator(queryset=HiddenFieldUniqueForDateModel.objects.all(), field='slug', date_field='published')>]):
+ TestSerializer():
id = IntegerField(label='ID', read_only=True)
slug = CharField(max_length=100)
published = DateTimeField(default=CreateOnlyDefault(<function now>), read_only=True)
+ class Meta:
+ validators = [<UniqueForDateValidator(queryset=HiddenFieldUniqueForDateModel.objects.all(), field='slug', date_field='published')>]
""")
assert repr(serializer) == expected