diff options
| author | Tom Christie | 2013-01-18 21:29:21 +0000 | 
|---|---|---|
| committer | Tom Christie | 2013-01-18 21:29:21 +0000 | 
| commit | 211bb89eecfadd6831a0c59852926f16ea6bf733 (patch) | |
| tree | 23592a21f3a67f1f96ad0e78e1a890d93559138e /rest_framework | |
| parent | 6385ac519defc8e434fd4e24a48a680845341cb7 (diff) | |
| download | django-rest-framework-211bb89eecfadd6831a0c59852926f16ea6bf733.tar.bz2 | |
Raise Validation Errors when relationships receive incorrect types. Fixes #590.
Diffstat (limited to 'rest_framework')
| -rw-r--r-- | rest_framework/relations.py | 20 | ||||
| -rw-r--r-- | rest_framework/tests/relations_hyperlink.py | 9 | ||||
| -rw-r--r-- | rest_framework/tests/relations_pk.py | 7 | ||||
| -rw-r--r-- | rest_framework/tests/relations_slug.py | 162 | 
4 files changed, 177 insertions, 21 deletions
diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 7ded3891..af63ceaa 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -177,7 +177,7 @@ class PrimaryKeyRelatedField(RelatedField):      default_error_messages = {          'does_not_exist': _("Invalid pk '%s' - object does not exist."), -        'invalid': _('Invalid value.'), +        'incorrect_type': _('Incorrect type.  Expected pk value, received %s.'),      }      # TODO: Remove these field hacks... @@ -208,7 +208,8 @@ class PrimaryKeyRelatedField(RelatedField):              msg = self.error_messages['does_not_exist'] % smart_unicode(data)              raise ValidationError(msg)          except (TypeError, ValueError): -            msg = self.error_messages['invalid'] +            received = type(data).__name__ +            msg = self.error_messages['incorrect_type'] % received              raise ValidationError(msg)      def field_to_native(self, obj, field_name): @@ -235,7 +236,7 @@ class ManyPrimaryKeyRelatedField(ManyRelatedField):      default_error_messages = {          'does_not_exist': _("Invalid pk '%s' - object does not exist."), -        'invalid': _('Invalid value.'), +        'incorrect_type': _('Incorrect type.  Expected pk value, received %s.'),      }      def prepare_value(self, obj): @@ -275,7 +276,8 @@ class ManyPrimaryKeyRelatedField(ManyRelatedField):              msg = self.error_messages['does_not_exist'] % smart_unicode(data)              raise ValidationError(msg)          except (TypeError, ValueError): -            msg = self.error_messages['invalid'] +            received = type(data).__name__ +            msg = self.error_messages['incorrect_type'] % received              raise ValidationError(msg)  ### Slug relationships @@ -333,7 +335,7 @@ class HyperlinkedRelatedField(RelatedField):          'incorrect_match': _('Invalid hyperlink - Incorrect URL match'),          'configuration_error': _('Invalid hyperlink due to configuration error'),          'does_not_exist': _("Invalid hyperlink - object does not exist."), -        'invalid': _('Invalid value.'), +        'incorrect_type': _('Incorrect type.  Expected url string, received %s.'),      }      def __init__(self, *args, **kwargs): @@ -397,8 +399,8 @@ class HyperlinkedRelatedField(RelatedField):          try:              http_prefix = value.startswith('http:') or value.startswith('https:')          except AttributeError: -            msg = self.error_messages['invalid'] -            raise ValidationError(msg) +            msg = self.error_messages['incorrect_type'] +            raise ValidationError(msg % type(value).__name__)          if http_prefix:              # If needed convert absolute URLs to relative path @@ -434,8 +436,8 @@ class HyperlinkedRelatedField(RelatedField):          except ObjectDoesNotExist:              raise ValidationError(self.error_messages['does_not_exist'])          except (TypeError, ValueError): -            msg = self.error_messages['invalid'] -            raise ValidationError(msg) +            msg = self.error_messages['incorrect_type'] +            raise ValidationError(msg % type(value).__name__)          return obj diff --git a/rest_framework/tests/relations_hyperlink.py b/rest_framework/tests/relations_hyperlink.py index 7d65eae7..6d137f68 100644 --- a/rest_framework/tests/relations_hyperlink.py +++ b/rest_framework/tests/relations_hyperlink.py @@ -215,6 +215,13 @@ class HyperlinkedForeignKeyTests(TestCase):          ]          self.assertEquals(serializer.data, expected) +    def test_foreign_key_update_incorrect_type(self): +        data = {'url': '/foreignkeysource/1/', 'name': u'source-1', 'target': 2} +        instance = ForeignKeySource.objects.get(pk=1) +        serializer = ForeignKeySourceSerializer(instance, data=data) +        self.assertFalse(serializer.is_valid()) +        self.assertEquals(serializer.errors, {'target': [u'Incorrect type.  Expected url string, received int.']}) +      def test_reverse_foreign_key_update(self):          data = {'url': '/foreignkeytarget/2/', 'name': u'target-2', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']}          instance = ForeignKeyTarget.objects.get(pk=2) @@ -227,7 +234,7 @@ class HyperlinkedForeignKeyTests(TestCase):          expected = [              {'url': '/foreignkeytarget/1/', 'name': u'target-1', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/2/', '/foreignkeysource/3/']},              {'url': '/foreignkeytarget/2/', 'name': u'target-2', 'sources': []}, -        ]         +        ]          self.assertEquals(new_serializer.data, expected)          serializer.save() diff --git a/rest_framework/tests/relations_pk.py b/rest_framework/tests/relations_pk.py index dd1e86b5..3391e60a 100644 --- a/rest_framework/tests/relations_pk.py +++ b/rest_framework/tests/relations_pk.py @@ -194,6 +194,13 @@ class PKForeignKeyTests(TestCase):          ]          self.assertEquals(serializer.data, expected) +    def test_foreign_key_update_incorrect_type(self): +        data = {'id': 1, 'name': u'source-1', 'target': 'foo'} +        instance = ForeignKeySource.objects.get(pk=1) +        serializer = ForeignKeySourceSerializer(instance, data=data) +        self.assertFalse(serializer.is_valid()) +        self.assertEquals(serializer.errors, {'target': [u'Incorrect type.  Expected pk value, received str.']}) +      def test_reverse_foreign_key_update(self):          data = {'id': 2, 'name': u'target-2', 'sources': [1, 3]}          instance = ForeignKeyTarget.objects.get(pk=2) diff --git a/rest_framework/tests/relations_slug.py b/rest_framework/tests/relations_slug.py index 503b61e8..37ccc75e 100644 --- a/rest_framework/tests/relations_slug.py +++ b/rest_framework/tests/relations_slug.py @@ -1,9 +1,23 @@  from django.test import TestCase  from rest_framework import serializers -from rest_framework.tests.models import NullableForeignKeySource, ForeignKeyTarget +from rest_framework.tests.models import NullableForeignKeySource, ForeignKeySource, ForeignKeyTarget -class NullableSlugSourceSerializer(serializers.ModelSerializer): +class ForeignKeyTargetSerializer(serializers.ModelSerializer): +    sources = serializers.ManySlugRelatedField(slug_field='name') + +    class Meta: +        model = ForeignKeyTarget + + +class ForeignKeySourceSerializer(serializers.ModelSerializer): +    target = serializers.SlugRelatedField(slug_field='name') + +    class Meta: +        model = ForeignKeySource + + +class NullableForeignKeySourceSerializer(serializers.ModelSerializer):      target = serializers.SlugRelatedField(slug_field='name', null=True)      class Meta: @@ -11,6 +25,132 @@ class NullableSlugSourceSerializer(serializers.ModelSerializer):  # TODO: M2M Tests, FKTests (Non-nulable), One2One +class PKForeignKeyTests(TestCase): +    def setUp(self): +        target = ForeignKeyTarget(name='target-1') +        target.save() +        new_target = ForeignKeyTarget(name='target-2') +        new_target.save() +        for idx in range(1, 4): +            source = ForeignKeySource(name='source-%d' % idx, target=target) +            source.save() + +    def test_foreign_key_retrieve(self): +        queryset = ForeignKeySource.objects.all() +        serializer = ForeignKeySourceSerializer(queryset) +        expected = [ +            {'id': 1, 'name': u'source-1', 'target': 'target-1'}, +            {'id': 2, 'name': u'source-2', 'target': 'target-1'}, +            {'id': 3, 'name': u'source-3', 'target': 'target-1'} +        ] +        self.assertEquals(serializer.data, expected) + +    def test_reverse_foreign_key_retrieve(self): +        queryset = ForeignKeyTarget.objects.all() +        serializer = ForeignKeyTargetSerializer(queryset) +        expected = [ +            {'id': 1, 'name': u'target-1', 'sources': ['source-1', 'source-2', 'source-3']}, +            {'id': 2, 'name': u'target-2', 'sources': []}, +        ] +        self.assertEquals(serializer.data, expected) + +    def test_foreign_key_update(self): +        data = {'id': 1, 'name': u'source-1', 'target': 'target-2'} +        instance = ForeignKeySource.objects.get(pk=1) +        serializer = ForeignKeySourceSerializer(instance, data=data) +        self.assertTrue(serializer.is_valid()) +        self.assertEquals(serializer.data, data) +        serializer.save() + +        # Ensure source 1 is updated, and everything else is as expected +        queryset = ForeignKeySource.objects.all() +        serializer = ForeignKeySourceSerializer(queryset) +        expected = [ +            {'id': 1, 'name': u'source-1', 'target': 'target-2'}, +            {'id': 2, 'name': u'source-2', 'target': 'target-1'}, +            {'id': 3, 'name': u'source-3', 'target': 'target-1'} +        ] +        self.assertEquals(serializer.data, expected) + +    def test_foreign_key_update_incorrect_type(self): +        data = {'id': 1, 'name': u'source-1', 'target': 123} +        instance = ForeignKeySource.objects.get(pk=1) +        serializer = ForeignKeySourceSerializer(instance, data=data) +        self.assertFalse(serializer.is_valid()) +        self.assertEquals(serializer.errors, {'target': [u'Object with name=123 does not exist.']}) + +    def test_reverse_foreign_key_update(self): +        data = {'id': 2, 'name': u'target-2', 'sources': ['source-1', 'source-3']} +        instance = ForeignKeyTarget.objects.get(pk=2) +        serializer = ForeignKeyTargetSerializer(instance, data=data) +        self.assertTrue(serializer.is_valid()) +        # We shouldn't have saved anything to the db yet since save +        # hasn't been called. +        queryset = ForeignKeyTarget.objects.all() +        new_serializer = ForeignKeyTargetSerializer(queryset) +        expected = [ +            {'id': 1, 'name': u'target-1', 'sources': ['source-1', 'source-2', 'source-3']}, +            {'id': 2, 'name': u'target-2', 'sources': []}, +        ] +        self.assertEquals(new_serializer.data, expected) + +        serializer.save() +        self.assertEquals(serializer.data, data) + +        # Ensure target 2 is update, and everything else is as expected +        queryset = ForeignKeyTarget.objects.all() +        serializer = ForeignKeyTargetSerializer(queryset) +        expected = [ +            {'id': 1, 'name': u'target-1', 'sources': ['source-2']}, +            {'id': 2, 'name': u'target-2', 'sources': ['source-1', 'source-3']}, +        ] +        self.assertEquals(serializer.data, expected) + +    def test_foreign_key_create(self): +        data = {'id': 4, 'name': u'source-4', 'target': 'target-2'} +        serializer = ForeignKeySourceSerializer(data=data) +        serializer.is_valid() +        self.assertTrue(serializer.is_valid()) +        obj = serializer.save() +        self.assertEquals(serializer.data, data) +        self.assertEqual(obj.name, u'source-4') + +        # Ensure source 4 is added, and everything else is as expected +        queryset = ForeignKeySource.objects.all() +        serializer = ForeignKeySourceSerializer(queryset) +        expected = [ +            {'id': 1, 'name': u'source-1', 'target': 'target-1'}, +            {'id': 2, 'name': u'source-2', 'target': 'target-1'}, +            {'id': 3, 'name': u'source-3', 'target': 'target-1'}, +            {'id': 4, 'name': u'source-4', 'target': 'target-2'}, +        ] +        self.assertEquals(serializer.data, expected) + +    def test_reverse_foreign_key_create(self): +        data = {'id': 3, 'name': u'target-3', 'sources': ['source-1', 'source-3']} +        serializer = ForeignKeyTargetSerializer(data=data) +        self.assertTrue(serializer.is_valid()) +        obj = serializer.save() +        self.assertEquals(serializer.data, data) +        self.assertEqual(obj.name, u'target-3') + +        # Ensure target 3 is added, and everything else is as expected +        queryset = ForeignKeyTarget.objects.all() +        serializer = ForeignKeyTargetSerializer(queryset) +        expected = [ +            {'id': 1, 'name': u'target-1', 'sources': ['source-2']}, +            {'id': 2, 'name': u'target-2', 'sources': []}, +            {'id': 3, 'name': u'target-3', 'sources': ['source-1', 'source-3']}, +        ] +        self.assertEquals(serializer.data, expected) + +    def test_foreign_key_update_with_invalid_null(self): +        data = {'id': 1, 'name': u'source-1', 'target': None} +        instance = ForeignKeySource.objects.get(pk=1) +        serializer = ForeignKeySourceSerializer(instance, data=data) +        self.assertFalse(serializer.is_valid()) +        self.assertEquals(serializer.errors, {'target': [u'Value may not be null']}) +  class SlugNullableForeignKeyTests(TestCase):      def setUp(self): @@ -24,7 +164,7 @@ class SlugNullableForeignKeyTests(TestCase):      def test_foreign_key_retrieve_with_null(self):          queryset = NullableForeignKeySource.objects.all() -        serializer = NullableSlugSourceSerializer(queryset) +        serializer = NullableForeignKeySourceSerializer(queryset)          expected = [              {'id': 1, 'name': u'source-1', 'target': 'target-1'},              {'id': 2, 'name': u'source-2', 'target': 'target-1'}, @@ -34,7 +174,7 @@ class SlugNullableForeignKeyTests(TestCase):      def test_foreign_key_create_with_valid_null(self):          data = {'id': 4, 'name': u'source-4', 'target': None} -        serializer = NullableSlugSourceSerializer(data=data) +        serializer = NullableForeignKeySourceSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, data) @@ -42,7 +182,7 @@ class SlugNullableForeignKeyTests(TestCase):          # Ensure source 4 is created, and everything else is as expected          queryset = NullableForeignKeySource.objects.all() -        serializer = NullableSlugSourceSerializer(queryset) +        serializer = NullableForeignKeySourceSerializer(queryset)          expected = [              {'id': 1, 'name': u'source-1', 'target': 'target-1'},              {'id': 2, 'name': u'source-2', 'target': 'target-1'}, @@ -58,7 +198,7 @@ class SlugNullableForeignKeyTests(TestCase):          """          data = {'id': 4, 'name': u'source-4', 'target': ''}          expected_data = {'id': 4, 'name': u'source-4', 'target': None} -        serializer = NullableSlugSourceSerializer(data=data) +        serializer = NullableForeignKeySourceSerializer(data=data)          self.assertTrue(serializer.is_valid())          obj = serializer.save()          self.assertEquals(serializer.data, expected_data) @@ -66,7 +206,7 @@ class SlugNullableForeignKeyTests(TestCase):          # Ensure source 4 is created, and everything else is as expected          queryset = NullableForeignKeySource.objects.all() -        serializer = NullableSlugSourceSerializer(queryset) +        serializer = NullableForeignKeySourceSerializer(queryset)          expected = [              {'id': 1, 'name': u'source-1', 'target': 'target-1'},              {'id': 2, 'name': u'source-2', 'target': 'target-1'}, @@ -78,14 +218,14 @@ class SlugNullableForeignKeyTests(TestCase):      def test_foreign_key_update_with_valid_null(self):          data = {'id': 1, 'name': u'source-1', 'target': None}          instance = NullableForeignKeySource.objects.get(pk=1) -        serializer = NullableSlugSourceSerializer(instance, data=data) +        serializer = NullableForeignKeySourceSerializer(instance, data=data)          self.assertTrue(serializer.is_valid())          self.assertEquals(serializer.data, data)          serializer.save()          # Ensure source 1 is updated, and everything else is as expected          queryset = NullableForeignKeySource.objects.all() -        serializer = NullableSlugSourceSerializer(queryset) +        serializer = NullableForeignKeySourceSerializer(queryset)          expected = [              {'id': 1, 'name': u'source-1', 'target': None},              {'id': 2, 'name': u'source-2', 'target': 'target-1'}, @@ -101,14 +241,14 @@ class SlugNullableForeignKeyTests(TestCase):          data = {'id': 1, 'name': u'source-1', 'target': ''}          expected_data = {'id': 1, 'name': u'source-1', 'target': None}          instance = NullableForeignKeySource.objects.get(pk=1) -        serializer = NullableSlugSourceSerializer(instance, data=data) +        serializer = NullableForeignKeySourceSerializer(instance, data=data)          self.assertTrue(serializer.is_valid())          self.assertEquals(serializer.data, expected_data)          serializer.save()          # Ensure source 1 is updated, and everything else is as expected          queryset = NullableForeignKeySource.objects.all() -        serializer = NullableSlugSourceSerializer(queryset) +        serializer = NullableForeignKeySourceSerializer(queryset)          expected = [              {'id': 1, 'name': u'source-1', 'target': None},              {'id': 2, 'name': u'source-2', 'target': 'target-1'},  | 
