Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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/branches/soc2009/http-wsgi-improvements@10995 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 45e50c00d98ccc015ab7d85377a1272bb159edcb 1 parent cb847aa
Christopher Cahoon ccahoon authored
20 django/db/models/query.py
View
@@ -190,7 +190,25 @@ def iterator(self):
index_start = len(extra_select)
aggregate_start = index_start + len(self.model._meta.fields)
- load_fields = only_load.get(self.model)
+ load_fields = []
+ # If only/defer clauses have been specified,
+ # build the list of fields that are to be loaded.
+ if only_load:
+ for field, model in self.model._meta.get_fields_with_model():
+ if model is None:
+ model = self.model
+ if field == self.model._meta.pk:
+ # Record the index of the primary key when it is found
+ pk_idx = len(load_fields)
+ try:
+ if field.name in only_load[model]:
+ # Add a field that has been explicitly included
+ load_fields.append(field.name)
+ except KeyError:
+ # Model wasn't explicitly listed in the only_load table
+ # Therefore, we need to load all fields from this model
+ load_fields.append(field.name)
+
skip = None
if load_fields and not fill_cache:
# Some fields have been deferred, so we have to initialise
10 django/db/models/sql/query.py
View
@@ -635,10 +635,10 @@ def deferred_to_data(self, target, callback):
# models.
workset = {}
for model, values in seen.iteritems():
- for field, f_model in model._meta.get_fields_with_model():
+ for field in model._meta.local_fields:
if field in values:
continue
- add_to_dict(workset, f_model or model, field)
+ add_to_dict(workset, model, field)
for model, values in must_include.iteritems():
# If we haven't included a model in workset, we don't add the
# corresponding must_include fields for that model, since an
@@ -657,6 +657,12 @@ def deferred_to_data(self, target, callback):
# included any fields, we have to make sure it's mentioned
# so that only the "must include" fields are pulled in.
seen[model] = values
+ # Now ensure that every model in the inheritance chain is mentioned
+ # in the parent list. Again, it must be mentioned to ensure that
+ # only "must include" fields are pulled in.
+ for model in orig_opts.get_parent_list():
+ if model not in seen:
+ seen[model] = set()
for model, values in seen.iteritems():
callback(target, model, values)
92 tests/modeltests/defer/models.py
View
@@ -17,6 +17,12 @@ class Primary(models.Model):
def __unicode__(self):
return self.name
+class Child(Primary):
+ pass
+
+class BigChild(Primary):
+ other = models.CharField(max_length=50)
+
def count_delayed_fields(obj, debug=False):
"""
Returns the number of delayed attributes on the given model instance.
@@ -33,7 +39,7 @@ def count_delayed_fields(obj, debug=False):
__test__ = {"API_TEST": """
To all outward appearances, instances with deferred fields look the same as
-normal instances when we examine attribut values. Therefore we test for the
+normal instances when we examine attribute values. Therefore we test for the
number of deferred fields on returned instances (by poking at the internals),
as a way to observe what is going on.
@@ -98,5 +104,89 @@ def count_delayed_fields(obj, debug=False):
>>> Primary.objects.all()
[<Primary: a new name>]
+# Regression for #10572 - A subclass with no extra fields can defer fields from the base class
+>>> _ = Child.objects.create(name="c1", value="foo", related=s1)
+
+# You can defer a field on a baseclass when the subclass has no fields
+>>> obj = Child.objects.defer("value").get(name="c1")
+>>> count_delayed_fields(obj)
+1
+>>> obj.name
+u"c1"
+>>> obj.value
+u"foo"
+>>> obj.name = "c2"
+>>> obj.save()
+
+# You can retrive a single column on a base class with no fields
+>>> obj = Child.objects.only("name").get(name="c2")
+>>> count_delayed_fields(obj)
+3
+>>> obj.name
+u"c2"
+>>> obj.value
+u"foo"
+>>> obj.name = "cc"
+>>> obj.save()
+
+>>> _ = BigChild.objects.create(name="b1", value="foo", related=s1, other="bar")
+
+# You can defer a field on a baseclass
+>>> obj = BigChild.objects.defer("value").get(name="b1")
+>>> count_delayed_fields(obj)
+1
+>>> obj.name
+u"b1"
+>>> obj.value
+u"foo"
+>>> obj.other
+u"bar"
+>>> obj.name = "b2"
+>>> obj.save()
+
+# You can defer a field on a subclass
+>>> obj = BigChild.objects.defer("other").get(name="b2")
+>>> count_delayed_fields(obj)
+1
+>>> obj.name
+u"b2"
+>>> obj.value
+u"foo"
+>>> obj.other
+u"bar"
+>>> obj.name = "b3"
+>>> obj.save()
+
+# You can retrieve a single field on a baseclass
+>>> obj = BigChild.objects.only("name").get(name="b3")
+>>> count_delayed_fields(obj)
+4
+>>> obj.name
+u"b3"
+>>> obj.value
+u"foo"
+>>> obj.other
+u"bar"
+>>> obj.name = "b4"
+>>> obj.save()
+
+# You can retrieve a single field on a baseclass
+>>> obj = BigChild.objects.only("other").get(name="b4")
+>>> count_delayed_fields(obj)
+4
+>>> obj.name
+u"b4"
+>>> obj.value
+u"foo"
+>>> obj.other
+u"bar"
+>>> obj.name = "bb"
+>>> obj.save()
+
+# Finally, we need to flush the app cache for the defer module.
+# Using only/defer creates some artifical entries in the app cache
+# that messes up later tests. Purge all entries, just to be sure.
+>>> from django.db.models.loading import cache
+>>> cache.app_models['defer'] = {}
"""}
Please sign in to comment.
Something went wrong with that request. Please try again.