diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 68e4cdf91..ebb01d21d 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -64,7 +64,7 @@ def is_simple_callable(obj): return len_args <= len_defaults -def get_attribute(instance, attrs): +def get_attribute(instance, attrs, required=True): """ Similar to Python's built in `getattr(instance, attr)`, but takes a list of nested attributes, instead of a single attribute. @@ -82,6 +82,12 @@ def get_attribute(instance, attrs): instance = getattr(instance, attr) except ObjectDoesNotExist: return None + # RelatedObjectDoesNotExist inherits from both ObjectDoesNotExist and + # AttributeError, so ObjectDoesNotExist has to come first + except (AttributeError, KeyError): + if not required: + raise SkipField + raise if is_simple_callable(instance): try: instance = instance() diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 2e7c51b22..4eba9cfa8 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -154,7 +154,7 @@ class RelatedField(Field): pass # Standard case, return the object instance. - return get_attribute(instance, self.source_attrs) + return get_attribute(instance, self.source_attrs, self.required) @property def choices(self): diff --git a/tests/test_relations_pk.py b/tests/test_relations_pk.py index ba75bd94f..935869479 100644 --- a/tests/test_relations_pk.py +++ b/tests/test_relations_pk.py @@ -340,6 +340,18 @@ class PKForeignKeyTests(TestCase): serializer = NullableForeignKeySourceSerializer() self.assertEqual(serializer.data['target'], None) + def test_foreign_key_not_required(self): + """ + Let's say we wanted to fill the non-nullable model field inside + Model.save(), we would make it empty and not required. + """ + class ModelSerializer(ForeignKeySourceSerializer): + class Meta(ForeignKeySourceSerializer.Meta): + extra_kwargs = {'target': {'required': False}} + serializer = ModelSerializer(data={'name': 'test'}) + serializer.is_valid(raise_exception=True) + self.assertNotIn('target', serializer.data) + class PKNullableForeignKeyTests(TestCase): def setUp(self):