Commit 384ddbec authored by Tim Graham's avatar Tim Graham
Browse files

Fixed #14368 -- Allowed setting a reverse OneToOne relation to None.

parent 5d898500
Loading
Loading
Loading
Loading
+28 −18
Original line number Diff line number Diff line
@@ -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,
@@ -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:
+16 −0
Original line number Diff line number Diff line
@@ -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) """