From c13f132a21f5a0902cef74dfea12e586561774ca Mon Sep 17 00:00:00 2001 From: Yuri Prezument Date: Tue, 18 Dec 2012 14:09:58 +0200 Subject: [PATCH 1/5] Failing test with partial serializer and foreign keys --- rest_framework/tests/serializer.py | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/rest_framework/tests/serializer.py b/rest_framework/tests/serializer.py index 780177aa0..693c81c29 100644 --- a/rest_framework/tests/serializer.py +++ b/rest_framework/tests/serializer.py @@ -297,6 +297,38 @@ class ModelValidationTests(TestCase): self.assertFalse(second_serializer.is_valid()) self.assertEqual(second_serializer.errors, {'title': [u'Album with this Title already exists.']}) + def test_foreign_key_with_partial(self): + """ + Test ModelSerializer validation with partial=True + + Specifically test foreign key validation. + """ + + album = Album(title='test') + album.save() + + class PhotoSerializer(serializers.ModelSerializer): + class Meta: + model = Photo + + photo_serializer = PhotoSerializer(data={'description': 'test', 'album': album.pk}) + self.assertTrue(photo_serializer.is_valid()) + photo = photo_serializer.save() + + # Updating only the album (foreign key) + photo_serializer = PhotoSerializer(instance=photo, data={'album': album.pk}, partial=True) + self.assertTrue(photo_serializer.is_valid()) + self.assertTrue(photo_serializer.save()) + + # Updating only the description + photo_serializer = PhotoSerializer(instance=photo, + data={'description': 'new'}, + partial=True) + + self.assertTrue(photo_serializer.is_valid()) + self.assertTrue(photo_serializer.save()) + + class RegexValidationTest(TestCase): def test_create_failed(self): From 5c680c36e435ffa29ba6ed5f439d22ea4e849c67 Mon Sep 17 00:00:00 2001 From: Mark Aaron Shirley Date: Wed, 19 Dec 2012 09:26:40 -0800 Subject: [PATCH 2/5] Check if RelatedField is required. --- rest_framework/fields.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 2060e3e70..b0dfdce8c 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -351,6 +351,11 @@ class RelatedField(WritableField): if self.read_only: return + if field_name not in data and self.required: + raise ValidationError(self.error_messages['required']) + elif field_name not in data: + return + value = data.get(field_name) if value in (None, '') and not self.null: From 5f9ecd1c7ad2f52ad5711d2a89bb1884f5b662f9 Mon Sep 17 00:00:00 2001 From: Reinout van Rees Date: Fri, 21 Dec 2012 10:42:40 +0100 Subject: [PATCH 3/5] slug_kwarg attribute doesn't work; it should be slug_url_kwarg --- docs/api-guide/generic-views.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 428323b89..27c7d3f60 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -7,11 +7,11 @@ > > — [Django Documentation][cite] -One of the key benefits of class based views is the way they allow you to compose bits of reusable behaviour. REST framework takes advantage of this by providing a number of pre-built views that provide for commonly used patterns. +One of the key benefits of class based views is the way they allow you to compose bits of reusable behaviour. REST framework takes advantage of this by providing a number of pre-built views that provide for commonly used patterns. The generic views provided by REST framework allow you to quickly build API views that map closely to your database models. -If the generic views don't suit the needs of your API, you can drop down to using the regular `APIView` class, or reuse the mixins and base classes used by the generic views to compose your own set of reusable generic views. +If the generic views don't suit the needs of your API, you can drop down to using the regular `APIView` class, or reuse the mixins and base classes used by the generic views to compose your own set of reusable generic views. ## Examples @@ -29,7 +29,7 @@ For more complex cases you might also want to override various methods on the vi model = User serializer_class = UserSerializer permission_classes = (IsAdminUser,) - + def get_paginate_by(self, queryset): """ Use smaller pagination for HTML representations. @@ -150,14 +150,14 @@ Provides a base view for acting on a single object, by combining REST framework' * `queryset` - The queryset that should be used when retrieving an object from this view. If unset, defaults to the default queryset manager for `self.model`. * `pk_kwarg` - The URL kwarg that should be used to look up objects by primary key. Defaults to `'pk'`. [Can only be set to non-default on Django 1.4+] -* `slug_kwarg` - The URL kwarg that should be used to look up objects by a slug. Defaults to `'slug'`. [Can only be set to non-default on Django 1.4+] +* `slug_url_kwarg` - The URL kwarg that should be used to look up objects by a slug. Defaults to `'slug'`. [Can only be set to non-default on Django 1.4+] * `slug_field` - The field on the model that should be used to look up objects by a slug. If used, this should typically be set to a field with `unique=True`. Defaults to `'slug'`. --- # Mixins -The mixin classes provide the actions that are used to provide the basic view behaviour. Note that the mixin classes provide action methods rather than defining the handler methods such as `.get()` and `.post()` directly. This allows for more flexible composition of behaviour. +The mixin classes provide the actions that are used to provide the basic view behaviour. Note that the mixin classes provide action methods rather than defining the handler methods such as `.get()` and `.post()` directly. This allows for more flexible composition of behaviour. ## ListModelMixin @@ -220,4 +220,4 @@ Should be mixed in with [SingleObjectAPIView]. [CreateModelMixin]: #createmodelmixin [RetrieveModelMixin]: #retrievemodelmixin [UpdateModelMixin]: #updatemodelmixin -[DestroyModelMixin]: #destroymodelmixin \ No newline at end of file +[DestroyModelMixin]: #destroymodelmixin From f8a1256b1c006ee9ab46645a11ef19123b429a56 Mon Sep 17 00:00:00 2001 From: Mark Aaron Shirley Date: Fri, 21 Dec 2012 11:33:01 -0800 Subject: [PATCH 4/5] Update RelatedField#field_from_native coding style --- rest_framework/fields.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 1059df918..dd90c3f86 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -351,13 +351,13 @@ class RelatedField(WritableField): if self.read_only: return - if field_name not in data and self.required: - raise ValidationError(self.error_messages['required']) - elif field_name not in data: + try: + value = data[field_name] + except KeyError: + if self.required: + raise ValidationError(self.error_messages['required']) return - value = data.get(field_name) - if value in (None, '') and not self.null: raise ValidationError('Value may not be null') elif value in (None, '') and self.null: From ed09d26fb8da2391cab4b096d276028384438eb6 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 23 Dec 2012 18:50:17 +0000 Subject: [PATCH 5/5] Update docs/topics/release-notes.md --- docs/topics/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 6dedc3d22..71fa3c03d 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -6,6 +6,10 @@ ## 2.1.x series +### Master + +* Bugfix: Related fields now respect the required flag, and may be required=False. + ### 2.1.12 **Date**: 21st Dec 2012