Commit 0a89a57f authored by Malcolm Tredinnick's avatar Malcolm Tredinnick
Browse files

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
parent dded5f52
Loading
Loading
Loading
Loading
+26 −23
Original line number Diff line number Diff line
@@ -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()]
        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:
                init_list.append(field.attname)
            if skip:
        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)
    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 += offset

    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
+20 −0
Original line number Diff line number Diff line
@@ -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'

"""
}