mirror of https://github.com/django/django.git
Fixed #10572 -- Corrected the operation of the defer() and only() clauses when used on inherited models.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@10926 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
5b40ee32e6
commit
512ee0f528
|
@ -190,7 +190,25 @@ class QuerySet(object):
|
||||||
index_start = len(extra_select)
|
index_start = len(extra_select)
|
||||||
aggregate_start = index_start + len(self.model._meta.fields)
|
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
|
skip = None
|
||||||
if load_fields and not fill_cache:
|
if load_fields and not fill_cache:
|
||||||
# Some fields have been deferred, so we have to initialise
|
# Some fields have been deferred, so we have to initialise
|
||||||
|
|
|
@ -635,10 +635,10 @@ class BaseQuery(object):
|
||||||
# models.
|
# models.
|
||||||
workset = {}
|
workset = {}
|
||||||
for model, values in seen.iteritems():
|
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:
|
if field in values:
|
||||||
continue
|
continue
|
||||||
add_to_dict(workset, f_model or model, field)
|
add_to_dict(workset, model, field)
|
||||||
for model, values in must_include.iteritems():
|
for model, values in must_include.iteritems():
|
||||||
# If we haven't included a model in workset, we don't add the
|
# If we haven't included a model in workset, we don't add the
|
||||||
# corresponding must_include fields for that model, since an
|
# corresponding must_include fields for that model, since an
|
||||||
|
@ -657,6 +657,12 @@ class BaseQuery(object):
|
||||||
# included any fields, we have to make sure it's mentioned
|
# included any fields, we have to make sure it's mentioned
|
||||||
# so that only the "must include" fields are pulled in.
|
# so that only the "must include" fields are pulled in.
|
||||||
seen[model] = values
|
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():
|
for model, values in seen.iteritems():
|
||||||
callback(target, model, values)
|
callback(target, model, values)
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,12 @@ class Primary(models.Model):
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
class Child(Primary):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class BigChild(Primary):
|
||||||
|
other = models.CharField(max_length=50)
|
||||||
|
|
||||||
def count_delayed_fields(obj, debug=False):
|
def count_delayed_fields(obj, debug=False):
|
||||||
"""
|
"""
|
||||||
Returns the number of delayed attributes on the given model instance.
|
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": """
|
__test__ = {"API_TEST": """
|
||||||
To all outward appearances, instances with deferred fields look the same as
|
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),
|
number of deferred fields on returned instances (by poking at the internals),
|
||||||
as a way to observe what is going on.
|
as a way to observe what is going on.
|
||||||
|
|
||||||
|
@ -98,5 +104,89 @@ Using defer() and only() with get() is also valid.
|
||||||
>>> Primary.objects.all()
|
>>> Primary.objects.all()
|
||||||
[<Primary: a new name>]
|
[<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'] = {}
|
||||||
|
|
||||||
"""}
|
"""}
|
||||||
|
|
Loading…
Reference in New Issue