Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #14694 -- Made ``defer()`` work with reverse relations

Reverse o2o fields are now usable with defer.
  • Loading branch information...
commit 6ebf115206289bce8f3d86318871faac13d6e835 1 parent 2a0e4c2
authored November 28, 2012 akaariai committed November 28, 2012
20  django/db/models/sql/query.py
@@ -599,17 +599,22 @@ def deferred_to_data(self, target, callback):
599 599
             for name in parts[:-1]:
600 600
                 old_model = cur_model
601 601
                 source = opts.get_field_by_name(name)[0]
602  
-                cur_model = source.rel.to
  602
+                if is_reverse_o2o(source):
  603
+                    cur_model = source.model
  604
+                else:
  605
+                    cur_model = source.rel.to
603 606
                 opts = cur_model._meta
604 607
                 # Even if we're "just passing through" this model, we must add
605 608
                 # both the current model's pk and the related reference field
606  
-                # to the things we select.
607  
-                must_include[old_model].add(source)
  609
+                # (if it's not a reverse relation) to the things we select.
  610
+                if not is_reverse_o2o(source):
  611
+                    must_include[old_model].add(source)
608 612
                 add_to_dict(must_include, cur_model, opts.pk)
609 613
             field, model, _, _ = opts.get_field_by_name(parts[-1])
610 614
             if model is None:
611 615
                 model = cur_model
612  
-            add_to_dict(seen, model, field)
  616
+            if not is_reverse_o2o(field):
  617
+                add_to_dict(seen, model, field)
613 618
 
614 619
         if defer:
615 620
             # We need to load all fields for each model, except those that
@@ -1983,3 +1988,10 @@ def add_to_dict(data, key, value):
1983 1988
         data[key].add(value)
1984 1989
     else:
1985 1990
         data[key] = set([value])
  1991
+
  1992
+def is_reverse_o2o(field):
  1993
+    """
  1994
+    A little helper to check if the given field is reverse-o2o. The field is
  1995
+    expected to be some sort of relation field or related object.
  1996
+    """
  1997
+    return not hasattr(field, 'rel') and field.field.unique
4  tests/regressiontests/defer_regress/models.py
@@ -55,6 +55,10 @@ class Feature(models.Model):
55 55
 class SpecialFeature(models.Model):
56 56
     feature = models.ForeignKey(Feature)
57 57
 
  58
+class OneToOneItem(models.Model):
  59
+    item = models.OneToOneField(Item, related_name="one_to_one_item")
  60
+    name = models.CharField(max_length=15)
  61
+
58 62
 class ItemAndSimpleItem(models.Model):
59 63
     item = models.ForeignKey(Item)
60 64
     simple = models.ForeignKey(SimpleItem)
31  tests/regressiontests/defer_regress/tests.py
@@ -9,7 +9,7 @@
9 9
 from django.test import TestCase
10 10
 
11 11
 from .models import (ResolveThis, Item, RelatedItem, Child, Leaf, Proxy,
12  
-    SimpleItem, Feature, ItemAndSimpleItem, SpecialFeature)
  12
+    SimpleItem, Feature, ItemAndSimpleItem, OneToOneItem, SpecialFeature)
13 13
 
14 14
 
15 15
 class DeferRegressionTest(TestCase):
@@ -111,6 +111,7 @@ def test_basic(self):
111 111
                 Item,
112 112
                 ItemAndSimpleItem,
113 113
                 Leaf,
  114
+                OneToOneItem,
114 115
                 Proxy,
115 116
                 RelatedItem,
116 117
                 ResolveThis,
@@ -147,6 +148,7 @@ def test_basic(self):
147 148
                 "Leaf_Deferred_name_value",
148 149
                 "Leaf_Deferred_second_child_id_value",
149 150
                 "Leaf_Deferred_value",
  151
+                "OneToOneItem",
150 152
                 "Proxy",
151 153
                 "RelatedItem",
152 154
                 "RelatedItem_Deferred_",
@@ -182,6 +184,33 @@ def test_resolve_columns(self):
182 184
         self.assertEqual(1, qs.count())
183 185
         self.assertEqual('Foobar', qs[0].name)
184 186
 
  187
+    def test_reverse_one_to_one_relations(self):
  188
+        # Refs #14694. Test reverse relations which are known unique (reverse
  189
+        # side has o2ofield or unique FK) - the o2o case
  190
+        item = Item.objects.create(name="first", value=42)
  191
+        o2o = OneToOneItem.objects.create(item=item, name="second")
  192
+        self.assertEqual(len(Item.objects.defer('one_to_one_item__name')), 1)
  193
+        self.assertEqual(len(Item.objects.select_related('one_to_one_item')), 1)
  194
+        self.assertEqual(len(Item.objects.select_related(
  195
+            'one_to_one_item').defer('one_to_one_item__name')), 1)
  196
+        self.assertEqual(len(Item.objects.select_related('one_to_one_item').defer('value')), 1)
  197
+        # Make sure that `only()` doesn't break when we pass in a unique relation,
  198
+        # rather than a field on the relation.
  199
+        self.assertEqual(len(Item.objects.only('one_to_one_item')), 1)
  200
+        with self.assertNumQueries(1):
  201
+            i = Item.objects.select_related('one_to_one_item')[0]
  202
+            self.assertEquals(i.one_to_one_item.pk, o2o.pk)
  203
+            self.assertEquals(i.one_to_one_item.name, "second")
  204
+        with self.assertNumQueries(1):
  205
+            i = Item.objects.select_related('one_to_one_item').defer(
  206
+                'value', 'one_to_one_item__name')[0]
  207
+            self.assertEquals(i.one_to_one_item.pk, o2o.pk)
  208
+            self.assertEquals(i.name, "first")
  209
+        with self.assertNumQueries(1):
  210
+            self.assertEquals(i.one_to_one_item.name, "second")
  211
+        with self.assertNumQueries(1):
  212
+            self.assertEquals(i.value, 42)
  213
+
185 214
     def test_defer_with_select_related(self):
186 215
         item1 = Item.objects.create(name="first", value=47)
187 216
         item2 = Item.objects.create(name="second", value=42)

2 notes on commit 6ebf115

Claude Paroz
Owner

Some deprecated "assertEqual*s*" leaked in the test...

Anssi Kääriäinen
Owner

Sorry. I wasn't actually aware that the "s" version was deprecated. Will fix.

Please sign in to comment.
Something went wrong with that request. Please try again.