Author: mtredinnick
Date: 2009-04-04 00:34:23 -0500 (Sat, 04 Apr 2009)
New Revision: 10383

Modified:
   django/trunk/django/db/models/query.py
   django/trunk/tests/regressiontests/defer_regress/models.py
Log:
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.

Modified: django/trunk/django/db/models/query.py
===================================================================
--- django/trunk/django/db/models/query.py      2009-04-04 03:21:31 UTC (rev 
10382)
+++ django/trunk/django/db/models/query.py      2009-04-04 05:34:23 UTC (rev 
10383)
@@ -908,31 +908,35 @@
         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 @@
                 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

Modified: django/trunk/tests/regressiontests/defer_regress/models.py
===================================================================
--- django/trunk/tests/regressiontests/defer_regress/models.py  2009-04-04 
03:21:31 UTC (rev 10382)
+++ django/trunk/tests/regressiontests/defer_regress/models.py  2009-04-04 
05:34:23 UTC (rev 10383)
@@ -17,6 +17,18 @@
 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 @@
 >>> 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'
+
 """
 }


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to