Ticket #13839: 13839-4.patch

File 13839-4.patch, 4.7 KB (added by Aymeric Augustin, 12 years ago)
  • django/db/models/fields/related.py

     
    249249        if instance is None:
    250250            return self
    251251        try:
    252             return getattr(instance, self.cache_name)
     252            related = getattr(instance, self.cache_name)
    253253        except AttributeError:
    254254            params = {'%s__pk' % self.related.field.name: instance._get_pk_val()}
    255             rel_obj = self.get_query_set(instance=instance).get(**params)
    256             setattr(instance, self.cache_name, rel_obj)
    257             return rel_obj
     255            try:
     256                related = self.get_query_set(instance=instance).get(**params)
     257            except self.related.model.DoesNotExist:
     258                related = None
     259            else:
     260                setattr(related, self.related.field.get_cache_name(), instance)
     261            setattr(instance, self.cache_name, related)
     262        if related is None:
     263            raise self.related.model.DoesNotExist
     264        else:
     265            return related
    258266
    259267    def __set__(self, instance, value):
    260268        if instance is None:
     
    331339    def __get__(self, instance, instance_type=None):
    332340        if instance is None:
    333341            return self
    334 
    335342        try:
    336343            return getattr(instance, self.cache_name)
    337344        except AttributeError:
     
    347354            else:
    348355                params = {'%s__exact' % self.field.rel.field_name: val}
    349356            qs = self.get_query_set(instance=instance)
     357            # Assuming the database enforces foreign keys, this won't fail.
    350358            rel_obj = qs.get(**params)
    351359            setattr(instance, self.cache_name, rel_obj)
    352360            return rel_obj
     
    385393            # populated the cache, then we don't care - we're only accessing
    386394            # the object to invalidate the accessor cache, so there's no
    387395            # need to populate the cache just to expire it again.
    388             related = getattr(instance, self.field.get_cache_name(), None)
     396            related = getattr(instance, self.cache_name, None)
    389397
    390398            # If we've got an old related object, we need to clear out its
    391399            # cache. This cache also might not exist if the related object
     
    405413        setattr(instance, self.field.attname, val)
    406414
    407415        # Since we already know what the related object is, seed the related
    408         # object cache now, too. This avoids another db hit if you get the
     416        # object caches now, too. This avoids another db hit if you get the
    409417        # object you just set.
    410         setattr(instance, self.field.get_cache_name(), value)
     418        setattr(instance, self.cache_name, value)
     419        if value is not None and not self.field.rel.multiple:
     420            setattr(value, self.field.related.get_cache_name(), instance)
    411421
    412422class ForeignRelatedObjectsDescriptor(object):
    413423    # This class provides the functionality that makes the related-object
  • tests/regressiontests/select_related_onetoone/tests.py

     
    7979        p1 = Product.objects.create(name="Django Plushie", image=im)
    8080        p2 = Product.objects.create(name="Talking Django Plushie")
    8181
    82         self.assertEqual(len(Product.objects.select_related("image")), 2)
     82        with self.assertNumQueries(1):
     83            result = sorted(Product.objects.select_related("image"), key=lambda x: x.name)
     84            self.assertEqual([p.name for p in result], ["Django Plushie", "Talking Django Plushie"])
     85
     86            self.assertEqual(p1.image, im)
     87            # Check for ticket #13839
     88            self.assertIsNone(p2.image)
     89
     90    def test_missing_reverse(self):
     91        """
     92        Ticket #13839: select_related() should NOT cache None
     93        for missing objects on a reverse 1-1 relation.
     94        """
     95        with self.assertNumQueries(1):
     96            user = User.objects.select_related('userprofile').get(username='bob')
     97            with self.assertRaises(UserProfile.DoesNotExist):
     98                user.userprofile
     99
     100    def test_nullable_missing_reverse(self):
     101        """
     102        Ticket #13839: select_related() should NOT cache None
     103        for missing objects on a reverse 0-1 relation.
     104        """
     105        Image.objects.create(name="imag1")
     106
     107        with self.assertNumQueries(1):
     108            image = Image.objects.select_related('product').get()
     109            with self.assertRaises(Product.DoesNotExist):
     110                image.product
Back to Top