mirror of https://github.com/django/django.git
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
This commit is contained in:
parent
dded5f52cc
commit
0a89a57ffc
|
@ -908,31 +908,35 @@ def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0,
|
|||
return None
|
||||
|
||||
restricted = requested is not None
|
||||
index_end = index_start + len(klass._meta.fields)
|
||||
fields = row[index_start:index_end]
|
||||
if not [x for x in fields if x is not None]:
|
||||
# If we only have a list of Nones, there was not related object.
|
||||
obj = None
|
||||
else:
|
||||
load_fields = only_load and only_load.get(klass) or None
|
||||
if load_fields:
|
||||
# Handle deferred fields.
|
||||
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)))
|
||||
load_fields = only_load and only_load.get(klass) or None
|
||||
if load_fields:
|
||||
# Handle deferred fields.
|
||||
skip = set()
|
||||
init_list = []
|
||||
pk_val = row[index_start + klass._meta.pk_index()]
|
||||
for field in klass._meta.fields:
|
||||
if field.name not in load_fields:
|
||||
skip.add(field.name)
|
||||
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:
|
||||
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:
|
||||
if not select_related_descend(f, restricted, requested):
|
||||
continue
|
||||
|
@ -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)
|
||||
return obj, index_end
|
||||
|
||||
|
||||
def delete_objects(seen_objs):
|
||||
"""
|
||||
Iterate through a list of seen classes, and remove any instances that are
|
||||
|
|
|
@ -17,6 +17,18 @@ class Item(models.Model):
|
|||
class RelatedItem(models.Model):
|
||||
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": """
|
||||
Deferred fields should really be deferred and not accidentally use the field's
|
||||
default value just because they aren't passed to __init__.
|
||||
|
@ -66,7 +78,15 @@ True
|
|||
>>> r.item == i
|
||||
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'
|
||||
|
||||
"""
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue