Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #10572 -- Corrected the operation of the defer() and only() cla…

…uses when used on inherited models.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10926 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 512ee0f52889eb7f624f309cdc61fab57ab73a7b 1 parent 5b40ee3
Russell Keith-Magee authored June 06, 2009
20  django/db/models/query.py
@@ -190,7 +190,25 @@ def iterator(self):
190 190
         index_start = len(extra_select)
191 191
         aggregate_start = index_start + len(self.model._meta.fields)
192 192
 
193  
-        load_fields = only_load.get(self.model)
  193
+        load_fields = []
  194
+        # If only/defer clauses have been specified,
  195
+        # build the list of fields that are to be loaded.
  196
+        if only_load:
  197
+            for field, model in self.model._meta.get_fields_with_model():
  198
+                if model is None:
  199
+                    model = self.model
  200
+                if field == self.model._meta.pk:
  201
+                    # Record the index of the primary key when it is found
  202
+                    pk_idx = len(load_fields)
  203
+                try:
  204
+                    if field.name in only_load[model]:
  205
+                        # Add a field that has been explicitly included
  206
+                        load_fields.append(field.name)
  207
+                except KeyError:
  208
+                    # Model wasn't explicitly listed in the only_load table
  209
+                    # Therefore, we need to load all fields from this model
  210
+                    load_fields.append(field.name)
  211
+
194 212
         skip = None
195 213
         if load_fields and not fill_cache:
196 214
             # Some fields have been deferred, so we have to initialise
10  django/db/models/sql/query.py
@@ -635,10 +635,10 @@ def deferred_to_data(self, target, callback):
635 635
             # models.
636 636
             workset = {}
637 637
             for model, values in seen.iteritems():
638  
-                for field, f_model in model._meta.get_fields_with_model():
  638
+                for field in model._meta.local_fields:
639 639
                     if field in values:
640 640
                         continue
641  
-                    add_to_dict(workset, f_model or model, field)
  641
+                    add_to_dict(workset, model, field)
642 642
             for model, values in must_include.iteritems():
643 643
                 # If we haven't included a model in workset, we don't add the
644 644
                 # corresponding must_include fields for that model, since an
@@ -657,6 +657,12 @@ def deferred_to_data(self, target, callback):
657 657
                     # included any fields, we have to make sure it's mentioned
658 658
                     # so that only the "must include" fields are pulled in.
659 659
                     seen[model] = values
  660
+            # Now ensure that every model in the inheritance chain is mentioned
  661
+            # in the parent list. Again, it must be mentioned to ensure that
  662
+            # only "must include" fields are pulled in.
  663
+            for model in orig_opts.get_parent_list():
  664
+                if model not in seen:
  665
+                    seen[model] = set()
660 666
             for model, values in seen.iteritems():
661 667
                 callback(target, model, values)
662 668
 
92  tests/modeltests/defer/models.py
@@ -17,6 +17,12 @@ class Primary(models.Model):
17 17
     def __unicode__(self):
18 18
         return self.name
19 19
 
  20
+class Child(Primary):
  21
+    pass
  22
+
  23
+class BigChild(Primary):
  24
+    other = models.CharField(max_length=50)
  25
+
20 26
 def count_delayed_fields(obj, debug=False):
21 27
     """
22 28
     Returns the number of delayed attributes on the given model instance.
@@ -33,7 +39,7 @@ def count_delayed_fields(obj, debug=False):
33 39
 
34 40
 __test__ = {"API_TEST": """
35 41
 To all outward appearances, instances with deferred fields look the same as
36  
-normal instances when we examine attribut values. Therefore we test for the
  42
+normal instances when we examine attribute values. Therefore we test for the
37 43
 number of deferred fields on returned instances (by poking at the internals),
38 44
 as a way to observe what is going on.
39 45
 
@@ -98,5 +104,89 @@ def count_delayed_fields(obj, debug=False):
98 104
 >>> Primary.objects.all()
99 105
 [<Primary: a new name>]
100 106
 
  107
+# Regression for #10572 - A subclass with no extra fields can defer fields from the base class
  108
+>>> _ = Child.objects.create(name="c1", value="foo", related=s1)
  109
+
  110
+# You can defer a field on a baseclass when the subclass has no fields
  111
+>>> obj = Child.objects.defer("value").get(name="c1")
  112
+>>> count_delayed_fields(obj)
  113
+1
  114
+>>> obj.name
  115
+u"c1"
  116
+>>> obj.value
  117
+u"foo"
  118
+>>> obj.name = "c2"
  119
+>>> obj.save()
  120
+
  121
+# You can retrive a single column on a base class with no fields
  122
+>>> obj = Child.objects.only("name").get(name="c2")
  123
+>>> count_delayed_fields(obj)
  124
+3
  125
+>>> obj.name
  126
+u"c2"
  127
+>>> obj.value
  128
+u"foo"
  129
+>>> obj.name = "cc"
  130
+>>> obj.save()
  131
+
  132
+>>> _ = BigChild.objects.create(name="b1", value="foo", related=s1, other="bar")
  133
+
  134
+# You can defer a field on a baseclass
  135
+>>> obj = BigChild.objects.defer("value").get(name="b1")
  136
+>>> count_delayed_fields(obj)
  137
+1
  138
+>>> obj.name
  139
+u"b1"
  140
+>>> obj.value
  141
+u"foo"
  142
+>>> obj.other
  143
+u"bar"
  144
+>>> obj.name = "b2"
  145
+>>> obj.save()
  146
+
  147
+# You can defer a field on a subclass
  148
+>>> obj = BigChild.objects.defer("other").get(name="b2")
  149
+>>> count_delayed_fields(obj)
  150
+1
  151
+>>> obj.name
  152
+u"b2"
  153
+>>> obj.value
  154
+u"foo"
  155
+>>> obj.other
  156
+u"bar"
  157
+>>> obj.name = "b3"
  158
+>>> obj.save()
  159
+
  160
+# You can retrieve a single field on a baseclass
  161
+>>> obj = BigChild.objects.only("name").get(name="b3")
  162
+>>> count_delayed_fields(obj)
  163
+4
  164
+>>> obj.name
  165
+u"b3"
  166
+>>> obj.value
  167
+u"foo"
  168
+>>> obj.other
  169
+u"bar"
  170
+>>> obj.name = "b4"
  171
+>>> obj.save()
  172
+
  173
+# You can retrieve a single field on a baseclass
  174
+>>> obj = BigChild.objects.only("other").get(name="b4")
  175
+>>> count_delayed_fields(obj)
  176
+4
  177
+>>> obj.name
  178
+u"b4"
  179
+>>> obj.value
  180
+u"foo"
  181
+>>> obj.other
  182
+u"bar"
  183
+>>> obj.name = "bb"
  184
+>>> obj.save()
  185
+
  186
+# Finally, we need to flush the app cache for the defer module.
  187
+# Using only/defer creates some artifical entries in the app cache
  188
+# that messes up later tests. Purge all entries, just to be sure.
  189
+>>> from django.db.models.loading import cache
  190
+>>> cache.app_models['defer'] = {}
101 191
 
102 192
 """}

0 notes on commit 512ee0f

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