Ticket #18153: 18153-2.patch

File 18153-2.patch, 4.3 KB (added by Aymeric Augustin, 12 years ago)
  • django/db/models/fields/related.py

    commit 59698e91a1c80caf5da652dab833a400dc58c4cd
    Author: Aymeric Augustin <aymeric.augustin@m4x.org>
    Date:   Tue Oct 9 14:04:17 2012 +0200
    
        Fixed #18153 -- Reverse OneToOne lookups on unsaved instances.
        
        Thanks David Hatch and Anssi Kääriäinen for their inputs.
    
    diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
    index 157640c..a7af237 100644
    a b class SingleRelatedObjectDescriptor(object):  
    261261        try:
    262262            rel_obj = getattr(instance, self.cache_name)
    263263        except AttributeError:
    264             params = {'%s__pk' % self.related.field.name: instance._get_pk_val()}
    265             try:
    266                 rel_obj = self.get_query_set(instance=instance).get(**params)
    267             except self.related.model.DoesNotExist:
     264            related_pk = instance._get_pk_val()
     265            if related_pk is None:
    268266                rel_obj = None
    269267            else:
    270                 setattr(rel_obj, self.related.field.get_cache_name(), instance)
     268                params = {'%s__pk' % self.related.field.name: related_pk}
     269                try:
     270                    rel_obj = self.get_query_set(instance=instance).get(**params)
     271                except self.related.model.DoesNotExist:
     272                    rel_obj = None
     273                else:
     274                    setattr(rel_obj, self.related.field.get_cache_name(), instance)
    271275            setattr(instance, self.cache_name, rel_obj)
    272276        if rel_obj is None:
    273277            raise self.related.model.DoesNotExist
    class SingleRelatedObjectDescriptor(object):  
    301305                    raise ValueError('Cannot assign "%r": instance is on database "%s", value is on database "%s"' %
    302306                                        (value, instance._state.db, value._state.db))
    303307
     308        related_pk = getattr(instance, self.related.field.rel.get_related_field().attname)
     309        if related_pk is None:
     310            raise ValueError('Cannot assign "%r": "%s" instance isn\'t saved in the database.' %
     311                                (value, self.related.opts.object_name))
     312
    304313        # Set the value of the related field to the value of the related object's related field
    305         setattr(value, self.related.field.attname, getattr(instance, self.related.field.rel.get_related_field().attname))
     314        setattr(value, self.related.field.attname, related_pk)
    306315
    307316        # Since we already know what the related object is, seed the related
    308317        # object caches now, too. This avoids another db hit if you get the
  • tests/regressiontests/one_to_one_regress/tests.py

    diff --git a/tests/regressiontests/one_to_one_regress/tests.py b/tests/regressiontests/one_to_one_regress/tests.py
    index eced885..615536b 100644
    a b class OneToOneRegressionTests(TestCase):  
    202202        with self.assertNumQueries(0):
    203203            with self.assertRaises(UndergroundBar.DoesNotExist):
    204204                self.p1.undergroundbar
     205
     206    def test_get_reverse_on_unsaved_object(self):
     207        """
     208        Regression for #18153 and #19089.
     209
     210        Accessing the reverse relation on an unsaved object
     211        always raises an exception.
     212        """
     213        p = Place()
     214
     215        # When there's no instance of the origin of the one-to-one
     216        with self.assertNumQueries(0):
     217            with self.assertRaises(UndergroundBar.DoesNotExist):
     218                p.undergroundbar
     219
     220        UndergroundBar.objects.create()
     221
     222        # When there's one instance of the origin
     223        # (p.undergroundbar used to return that instance)
     224        with self.assertNumQueries(0):
     225            with self.assertRaises(UndergroundBar.DoesNotExist):
     226                p.undergroundbar
     227
     228        UndergroundBar.objects.create()
     229
     230        # When there are several instances of the origin
     231        with self.assertNumQueries(0):
     232            with self.assertRaises(UndergroundBar.DoesNotExist):
     233                p.undergroundbar
     234
     235    def test_set_reverse_on_unsaved_object(self):
     236        """
     237        Writing to the reverse relation on an unsaved object
     238        is impossible too.
     239        """
     240        p = Place()
     241        b = UndergroundBar.objects.create()
     242        with self.assertNumQueries(0):
     243            with self.assertRaises(ValueError):
     244                p.undergroundbar = b
Back to Top