Kaydet (Commit) 0a89a57f authored tarafından Malcolm Tredinnick's avatar Malcolm Tredinnick

Fixed deferred fields and select_related() interaction.

Loading related models when some fields were deferred was resulting in
incorrect offsets being used into the results row, causing the wrong data to be
assigned to attributes.

Refs #10710. This fixes the first of two bugs reported there.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10383 bcc190cf-cafb-0310-a4f2-bffc1f526a37
üst dded5f52
...@@ -908,31 +908,35 @@ def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0, ...@@ -908,31 +908,35 @@ def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0,
return None return None
restricted = requested is not None restricted = requested is not None
index_end = index_start + len(klass._meta.fields) load_fields = only_load and only_load.get(klass) or None
fields = row[index_start:index_end] if load_fields:
if not [x for x in fields if x is not None]: # Handle deferred fields.
# If we only have a list of Nones, there was not related object. skip = set()
obj = None init_list = []
else: pk_val = row[index_start + klass._meta.pk_index()]
load_fields = only_load and only_load.get(klass) or None for field in klass._meta.fields:
if load_fields: if field.name not in load_fields:
# Handle deferred fields. skip.add(field.name)
skip = set()
init_list = []
pk_val = fields[klass._meta.pk_index()]
for field in klass._meta.fields:
if field.name not in load_fields:
skip.add(field.name)
else:
init_list.append(field.attname)
if skip:
klass = deferred_class_factory(klass, skip)
obj = klass(**dict(zip(init_list, fields)))
else: else:
obj = klass(*fields) init_list.append(field.attname)
field_count = len(init_list)
fields = row[index_start : index_start + field_count]
if fields == (None,) * field_count:
obj = None
elif skip:
klass = deferred_class_factory(klass, skip)
obj = klass(**dict(zip(init_list, fields)))
else: else:
obj = klass(*fields) obj = klass(*fields)
index_end += offset else:
field_count = len(klass._meta.fields)
fields = row[index_start : index_start + field_count]
if fields == (None,) * field_count:
obj = None
else:
obj = klass(*fields)
index_end = index_start + field_count + offset
for f in klass._meta.fields: for f in klass._meta.fields:
if not select_related_descend(f, restricted, requested): if not select_related_descend(f, restricted, requested):
continue continue
...@@ -948,7 +952,6 @@ def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0, ...@@ -948,7 +952,6 @@ def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0,
setattr(obj, f.get_cache_name(), rel_obj) setattr(obj, f.get_cache_name(), rel_obj)
return obj, index_end return obj, index_end
def delete_objects(seen_objs): def delete_objects(seen_objs):
""" """
Iterate through a list of seen classes, and remove any instances that are Iterate through a list of seen classes, and remove any instances that are
......
...@@ -17,6 +17,18 @@ class Item(models.Model): ...@@ -17,6 +17,18 @@ class Item(models.Model):
class RelatedItem(models.Model): class RelatedItem(models.Model):
item = models.ForeignKey(Item) item = models.ForeignKey(Item)
class Child(models.Model):
name = models.CharField(max_length=10)
value = models.IntegerField()
class Leaf(models.Model):
name = models.CharField(max_length=10)
child = models.ForeignKey(Child)
value = models.IntegerField(default=42)
def __unicode__(self):
return self.name
__test__ = {"regression_tests": """ __test__ = {"regression_tests": """
Deferred fields should really be deferred and not accidentally use the field's Deferred fields should really be deferred and not accidentally use the field's
default value just because they aren't passed to __init__. default value just because they aren't passed to __init__.
...@@ -66,7 +78,15 @@ True ...@@ -66,7 +78,15 @@ True
>>> r.item == i >>> r.item == i
True True
Some further checks for select_related() and inherited model behaviour
(regression for #10710).
>>> c1 = Child.objects.create(name="c1", value=42)
>>> obj = Leaf.objects.create(name="l1", child=c1)
>>> obj = Leaf.objects.only("name", "child").select_related()[0]
>>> obj.child.name
u'c1'
""" """
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment