Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #18177 -- Cached known related instances.

This was recently fixed for one-to-one relations; this patch adds
support for foreign keys. Thanks kaiser.yann for the report and
the initial version of the patch.
  • Loading branch information...
commit 1e6c3368f2517f32b0651d68277ea8c9ef81d9b2 1 parent 3b2993e
Aymeric Augustin authored May 24, 2012
63  django/db/models/fields/related.py
@@ -237,13 +237,18 @@ def get_query_set(self, **db_hints):
@@ -324,17 +329,23 @@ def get_query_set(self, **db_hints):
@@ -467,18 +478,24 @@ def get_query_set(self):
21  django/db/models/query.py
@@ -41,6 +41,7 @@ def __init__(self, model=None, query=None, using=None):
41 41
         self._for_write = False
42 42
         self._prefetch_related_lookups = []
43 43
         self._prefetch_done = False
  44
+        self._known_related_object = None       # (attname, rel_obj)
44 45
 
45 46
     ########################
46 47
     # PYTHON MAGIC METHODS #
@@ -282,9 +283,10 @@ def iterator(self):
282 283
                     init_list.append(field.attname)
283 284
             model_cls = deferred_class_factory(self.model, skip)
284 285
 
285  
-        # Cache db and model outside the loop
  286
+        # Cache db, model and known_related_object outside the loop
286 287
         db = self.db
287 288
         model = self.model
  289
+        kro_attname, kro_instance = self._known_related_object or (None, None)
288 290
         compiler = self.query.get_compiler(using=db)
289 291
         if fill_cache:
290 292
             klass_info = get_klass_info(model, max_depth=max_depth,
@@ -294,12 +296,12 @@ def iterator(self):
294 296
                 obj, _ = get_cached_row(row, index_start, db, klass_info,
295 297
                                         offset=len(aggregate_select))
296 298
             else:
  299
+                # Omit aggregates in object creation.
  300
+                row_data = row[index_start:aggregate_start]
297 301
                 if skip:
298  
-                    row_data = row[index_start:aggregate_start]
299 302
                     obj = model_cls(**dict(zip(init_list, row_data)))
300 303
                 else:
301  
-                    # Omit aggregates in object creation.
302  
-                    obj = model(*row[index_start:aggregate_start])
  304
+                    obj = model(*row_data)
303 305
 
304 306
                 # Store the source database of the object
305 307
                 obj._state.db = db
@@ -313,7 +315,11 @@ def iterator(self):
313 315
             # Add the aggregates to the model
314 316
             if aggregate_select:
315 317
                 for i, aggregate in enumerate(aggregate_select):
316  
-                    setattr(obj, aggregate, row[i+aggregate_start])
  318
+                    setattr(obj, aggregate, row[i + aggregate_start])
  319
+
  320
+            # Add the known related object to the model, if there is one
  321
+            if kro_instance:
  322
+                setattr(obj, kro_attname, kro_instance)
317 323
 
318 324
             yield obj
319 325
 
@@ -864,6 +870,7 @@ def _clone(self, klass=None, setup=False, **kwargs):
864 870
         c = klass(model=self.model, query=query, using=self._db)
865 871
         c._for_write = self._for_write
866 872
         c._prefetch_related_lookups = self._prefetch_related_lookups[:]
  873
+        c._known_related_object = self._known_related_object
867 874
         c.__dict__.update(kwargs)
868 875
         if setup and hasattr(c, '_setup_query'):
869 876
             c._setup_query()
@@ -1781,9 +1788,7 @@ def prefetch_one_level(instances, prefetcher, attname):
1781 1788
     rel_obj_cache = {}
1782 1789
     for rel_obj in all_related_objects:
1783 1790
         rel_attr_val = rel_obj_attr(rel_obj)
1784  
-        if rel_attr_val not in rel_obj_cache:
1785  
-            rel_obj_cache[rel_attr_val] = []
1786  
-        rel_obj_cache[rel_attr_val].append(rel_obj)
  1791
+        rel_obj_cache.setdefault(rel_attr_val, []).append(rel_obj)
1787 1792
 
1788 1793
     for obj in instances:
1789 1794
         instance_attr_val = instance_attr(obj)
18  docs/releases/1.5.txt
@@ -44,6 +44,24 @@ reasons or when trying to avoid overwriting concurrent changes.
44 44
 See the :meth:`Model.save() <django.db.models.Model.save()>` documentation for
45 45
 more details.
46 46
 
  47
+Caching of related model instances
  48
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  49
+
  50
+When traversing relations, the ORM will avoid re-fetching objects that were
  51
+previously loaded. For example, with the tutorial's models::
  52
+
  53
+    >>> first_poll = Poll.objects.all()[0]
  54
+    >>> first_choice = first_poll.choice_set.all()[0]
  55
+    >>> first_choice.poll is first_poll
  56
+    True
  57
+
  58
+In Django 1.5, the third line no longer triggers a new SQL query to fetch
  59
+``first_choice.poll``; it was set when by the second line.
  60
+
  61
+For one-to-one relationships, both sides can be cached. For many-to-one
  62
+relationships, only the single side of the relationship can be cached. This
  63
+is particularly helpful in combination with ``prefetch_related``.
  64
+
47 65
 Minor features
48 66
 ~~~~~~~~~~~~~~
49 67
 
0  tests/modeltests/known_related_objects/__init__.py
No changes.
65  tests/modeltests/known_related_objects/fixtures/tournament.json
... ...
@@ -0,0 +1,65 @@
19  tests/modeltests/known_related_objects/models.py
... ...
@@ -0,0 +1,19 @@
88  tests/modeltests/known_related_objects/tests.py
... ...
@@ -0,0 +1,88 @@

0 notes on commit 1e6c336

Please sign in to comment.
Something went wrong with that request. Please try again.