Loading django/db/models/fields/related.py +15 −6 Original line number Diff line number Diff line Loading @@ -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: Loading Loading @@ -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 Loading tests/regressiontests/one_to_one_regress/tests.py +40 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
django/db/models/fields/related.py +15 −6 Original line number Diff line number Diff line Loading @@ -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: Loading Loading @@ -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 Loading
tests/regressiontests/one_to_one_regress/tests.py +40 −0 Original line number Diff line number Diff line Loading @@ -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