Loading django/db/models/fields/related_descriptors.py +28 −18 Original line number Diff line number Diff line Loading @@ -376,14 +376,24 @@ class ReverseOneToOneDescriptor(object): # If null=True, we can assign null here, but otherwise the value needs # to be an instance of the related class. if value is None and self.related.field.null is False: if value is None: if self.related.field.null: # Update the cached related instance (if any) & clear the cache. try: rel_obj = getattr(instance, self.cache_name) except AttributeError: pass else: delattr(instance, self.cache_name) setattr(rel_obj, self.related.field.name, None) else: raise ValueError( 'Cannot assign None: "%s.%s" does not allow null values.' % ( instance._meta.object_name, self.related.get_accessor_name(), ) ) elif value is not None and not isinstance(value, self.related.related_model): elif not isinstance(value, self.related.related_model): raise ValueError( 'Cannot assign "%r": "%s.%s" must be a "%s" instance.' % ( value, Loading @@ -392,7 +402,7 @@ class ReverseOneToOneDescriptor(object): self.related.related_model._meta.object_name, ) ) elif value is not None: else: if instance._state.db is None: instance._state.db = router.db_for_write(instance.__class__, instance=value) elif value._state.db is None: Loading tests/one_to_one/tests.py +16 −0 Original line number Diff line number Diff line Loading @@ -186,6 +186,22 @@ class OneToOneTests(TestCase): self.assertEqual(self.p1.restaurant, self.r1) self.assertEqual(self.p1.bar, self.b1) def test_assign_none_reverse_relation(self): p = Place.objects.get(name="Demon Dogs") # Assigning None succeeds if field is null=True. ug_bar = UndergroundBar.objects.create(place=p, serves_cocktails=False) p.undergroundbar = None self.assertIsNone(ug_bar.place) ug_bar.save() ug_bar.refresh_from_db() self.assertIsNone(ug_bar.place) def test_assign_none_null_reverse_relation(self): p = Place.objects.get(name="Demon Dogs") # Assigning None doesn't throw AttributeError if there isn't a related # UndergroundBar. p.undergroundbar = None def test_related_object_cache(self): """ Regression test for #6886 (the related-object cache) """ Loading Loading
django/db/models/fields/related_descriptors.py +28 −18 Original line number Diff line number Diff line Loading @@ -376,14 +376,24 @@ class ReverseOneToOneDescriptor(object): # If null=True, we can assign null here, but otherwise the value needs # to be an instance of the related class. if value is None and self.related.field.null is False: if value is None: if self.related.field.null: # Update the cached related instance (if any) & clear the cache. try: rel_obj = getattr(instance, self.cache_name) except AttributeError: pass else: delattr(instance, self.cache_name) setattr(rel_obj, self.related.field.name, None) else: raise ValueError( 'Cannot assign None: "%s.%s" does not allow null values.' % ( instance._meta.object_name, self.related.get_accessor_name(), ) ) elif value is not None and not isinstance(value, self.related.related_model): elif not isinstance(value, self.related.related_model): raise ValueError( 'Cannot assign "%r": "%s.%s" must be a "%s" instance.' % ( value, Loading @@ -392,7 +402,7 @@ class ReverseOneToOneDescriptor(object): self.related.related_model._meta.object_name, ) ) elif value is not None: else: if instance._state.db is None: instance._state.db = router.db_for_write(instance.__class__, instance=value) elif value._state.db is None: Loading
tests/one_to_one/tests.py +16 −0 Original line number Diff line number Diff line Loading @@ -186,6 +186,22 @@ class OneToOneTests(TestCase): self.assertEqual(self.p1.restaurant, self.r1) self.assertEqual(self.p1.bar, self.b1) def test_assign_none_reverse_relation(self): p = Place.objects.get(name="Demon Dogs") # Assigning None succeeds if field is null=True. ug_bar = UndergroundBar.objects.create(place=p, serves_cocktails=False) p.undergroundbar = None self.assertIsNone(ug_bar.place) ug_bar.save() ug_bar.refresh_from_db() self.assertIsNone(ug_bar.place) def test_assign_none_null_reverse_relation(self): p = Place.objects.get(name="Demon Dogs") # Assigning None doesn't throw AttributeError if there isn't a related # UndergroundBar. p.undergroundbar = None def test_related_object_cache(self): """ Regression test for #6886 (the related-object cache) """ Loading