Commits

Anonymous committed 2d073ff

Added a tagging.generic module for working with list of objects which have generic relations, containing a fetch_content_objects function

Comments (0)

Files changed (2)

 SVN Trunk Changes:
 ------------------
 
+* Added a ``tagging.generic`` module for working with list of objects
+  which have generic relations, containing a ``fetch_content_objects``
+  function for retrieving content objects for a list of ``TaggedItem``s
+  using ``number_of_content_types + 1`` queries rather than the
+  ``number_of_tagged_items * 2`` queries you'd get by iterating over the
+  list and accessing each item's ``object`` attribute.
+
 * Added a ``usage`` method to ``ModelTagManager``.
 
 * ``TaggedItemManager``'s methods now accept a ``QuerySet`` or a

tagging/generic.py

+from django.contrib.contenttypes.models import ContentType
+
+def fetch_content_objects(tagged_items, select_related_for=None):
+    """
+    Retrieves ``ContentType``s and content objects for the given list of
+    ``TaggedItems``, grouping the retrieval of content objects by model
+    to reduce the number of queries executed.
+
+    This results in ``number_of_content_types + 1`` queries rather than
+    the ``number_of_tagged_items * 2`` queries you'd get by iterating
+    over the list and accessing each item's ``object`` attribute.
+    """
+    if select_related_for is None: select_related_for = []
+
+    # Group content object pks by their content type pks
+    objects = {}
+    for item in tagged_items:
+        objects.setdefault(item.content_type_id, []).append(item.object_id)
+
+    # Retrieve content types and content objects in bulk
+    content_types = ContentType._default_manager.in_bulk(objects.keys())
+    for content_type_pk, object_pks in objects.items():
+        model = content_types[content_type_pk].model_class()
+        if not select_related_for:
+            objects[content_type_pk] = model._default_manager.in_bulk(object_pks)
+        elif content_types[content_type_pk].model in select_related_for:
+            objects[content_type_pk] = model._default_manager.select_related().in_bulk(object_pks)
+
+    # Set content types and content objects in the appropriate cache
+    # attributes, so accessing the 'content_type' and 'object'
+    # attributes on each tagged item won't result in further database
+    # hits.
+    for item in tagged_items:
+        item._object_cache = objects[item.content_type_id][item.object_id]
+        item._content_type_cache = content_types[item.content_type_id]