From 73425c31ce732646c6b2299feba4b913d9e93387 Mon Sep 17 00:00:00 2001 From: Sean Hayes Date: Wed, 17 Feb 2016 04:59:53 -0800 Subject: [PATCH] Permit changing existing value on a ToOneField to None. (Closes #1449) --- docs/release_notes/dev.rst | 2 +- tastypie/resources.py | 12 ++++++++++-- tests/core/tests/resources.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/docs/release_notes/dev.rst b/docs/release_notes/dev.rst index 8ac649786..3b7387ade 100644 --- a/docs/release_notes/dev.rst +++ b/docs/release_notes/dev.rst @@ -7,4 +7,4 @@ copied to the release notes for the next release. Bugfixes -------- -* list of issues/PRs +* Permit changing existing value on a ToOneField to None. (Closes #1449) diff --git a/tastypie/resources.py b/tastypie/resources.py index 6263e349a..486d76f51 100644 --- a/tastypie/resources.py +++ b/tastypie/resources.py @@ -25,6 +25,12 @@ except (ImproperlyConfigured, ImportError): GeometryField = None from django.db.models.constants import LOOKUP_SEP +try: + from django.db.models.fields.related import\ + SingleRelatedObjectDescriptor as ReverseOneToOneDescriptor +except ImportError: + from django.db.models.fields.related_descriptors import\ + ReverseOneToOneDescriptor from django.db.models.sql.constants import QUERY_TERMS from django.http import HttpResponse, HttpResponseNotFound, Http404 from django.utils import six @@ -944,10 +950,12 @@ def full_hydrate(self, bundle): setattr(bundle.obj, field_object.attribute, value.obj) except (ValueError, ObjectDoesNotExist): bundle.related_objects_to_save[field_object.attribute] = value.obj + elif field_object.null: + if not isinstance(getattr(bundle.obj.__class__, field_object.attribute, None), ReverseOneToOneDescriptor): + # only update if not a reverse one to one field + setattr(bundle.obj, field_object.attribute, value) elif field_object.blank: continue - elif field_object.null: - setattr(bundle.obj, field_object.attribute, value) return bundle diff --git a/tests/core/tests/resources.py b/tests/core/tests/resources.py index 4fab4f273..5802bb97e 100644 --- a/tests/core/tests/resources.py +++ b/tests/core/tests/resources.py @@ -542,6 +542,37 @@ def test_full_hydrate(self): self.assertEquals(hydrated.obj.name, "Daniel") + def test_full_hydrate__can_put_null_to_clear_related_value(self): + class RelatedBasicResource(BasicResource): + parent = fields.ToOneField(BasicResource, 'parent', null=True, blank=True) + basic = RelatedBasicResource() + basic_bundle_1 = Bundle(data={ + 'name': 'Daniel', + 'view_count': 6, + 'date_joined': None, + 'parent': None + }) + basic_bundle_1.obj = Mock() + basic_bundle_1.obj.date_joined = aware_datetime(2010, 2, 15, 12, 0, 0) + basic_bundle_1.obj.parent = Mock() + + self.assertEqual(basic_bundle_1.data['date_joined'], None) + self.assertEqual(basic_bundle_1.data['parent'], None) + self.assertNotEqual(basic_bundle_1.obj.date_joined, None) + self.assertNotEqual(basic_bundle_1.obj.parent, None) + + # Now load up the data. + hydrated = basic.full_hydrate(basic_bundle_1) + + self.assertEqual(hydrated.data['name'], 'Daniel') + self.assertEqual(hydrated.data['view_count'], 6) + self.assertEqual(hydrated.data['date_joined'], None) + self.assertEqual(hydrated.data['parent'], None) + self.assertEqual(hydrated.obj.name, 'Daniel') + self.assertEqual(hydrated.obj.view_count, 6) + self.assertEqual(hydrated.obj.date_joined, None) + self.assertEqual(hydrated.obj.parent, None) + def test_obj_get_list(self): basic = BasicResource() bundle = Bundle()