Commit 3190abcd authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

Fixed #18153 -- Reverse OneToOne lookups on unsaved instances.

Thanks David Hatch and Anssi Kääriäinen for their inputs.
parent c9b4e9ac
Loading
Loading
Loading
Loading
+15 −6
Original line number Diff line number Diff line
@@ -261,7 +261,11 @@ class SingleRelatedObjectDescriptor(object):
        try:
            rel_obj = getattr(instance, self.cache_name)
        except AttributeError:
            params = {'%s__pk' % self.related.field.name: instance._get_pk_val()}
            related_pk = instance._get_pk_val()
            if related_pk is None:
                rel_obj = None
            else:
                params = {'%s__pk' % self.related.field.name: related_pk}
                try:
                    rel_obj = self.get_query_set(instance=instance).get(**params)
                except self.related.model.DoesNotExist:
@@ -301,8 +305,13 @@ class SingleRelatedObjectDescriptor(object):
                    raise ValueError('Cannot assign "%r": instance is on database "%s", value is on database "%s"' %
                                        (value, instance._state.db, value._state.db))

        related_pk = getattr(instance, self.related.field.rel.get_related_field().attname)
        if related_pk is None:
            raise ValueError('Cannot assign "%r": "%s" instance isn\'t saved in the database.' %
                                (value, self.related.opts.object_name))

        # Set the value of the related field to the value of the related object's related field
        setattr(value, self.related.field.attname, getattr(instance, self.related.field.rel.get_related_field().attname))
        setattr(value, self.related.field.attname, related_pk)

        # Since we already know what the related object is, seed the related
        # object caches now, too. This avoids another db hit if you get the
+40 −0
Original line number Diff line number Diff line
@@ -202,3 +202,43 @@ class OneToOneRegressionTests(TestCase):
        with self.assertNumQueries(0):
            with self.assertRaises(UndergroundBar.DoesNotExist):
                self.p1.undergroundbar

    def test_get_reverse_on_unsaved_object(self):
        """
        Regression for #18153 and #19089.

        Accessing the reverse relation on an unsaved object
        always raises an exception.
        """
        p = Place()

        # When there's no instance of the origin of the one-to-one
        with self.assertNumQueries(0):
            with self.assertRaises(UndergroundBar.DoesNotExist):
                p.undergroundbar

        UndergroundBar.objects.create()

        # When there's one instance of the origin
        # (p.undergroundbar used to return that instance)
        with self.assertNumQueries(0):
            with self.assertRaises(UndergroundBar.DoesNotExist):
                p.undergroundbar

        UndergroundBar.objects.create()

        # When there are several instances of the origin
        with self.assertNumQueries(0):
            with self.assertRaises(UndergroundBar.DoesNotExist):
                p.undergroundbar

    def test_set_reverse_on_unsaved_object(self):
        """
        Writing to the reverse relation on an unsaved object
        is impossible too.
        """
        p = Place()
        b = UndergroundBar.objects.create()
        with self.assertNumQueries(0):
            with self.assertRaises(ValueError):
                p.undergroundbar = b