from django.db.models.query import QuerySet from django.db.models import Manager from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.generic import GenericForeignKey class GFKManager(Manager): """ A manager that returns a GFKQuerySet instead of a regular QuerySet. """ def get_query_set(self): return GFKQuerySet(self.model) class GFKQuerySet(QuerySet): """ A QuerySet with a fetch_generic_relations() method to bulk fetch all generic related items. Similar to select_related(), but for generic foreign keys. Based on http://www.djangosnippets.org/snippets/984/ """ def fetch_generic_relations(self): qs = self._clone() gfk_fields = [g for g in self.model._meta.virtual_fields if isinstance(g, GenericForeignKey)] ct_map = {} item_map = {} for item in qs: for gfk in gfk_fields: ct_id_field = self.model._meta.get_field(gfk.ct_field).column ct_map.setdefault( (ct_id_field, getattr(item, ct_id_field)), {} )[getattr(item, gfk.fk_field)] = (gfk.name, item.id) item_map[item.id] = item for (ct_id_field, ct_id), items_ in ct_map.items(): ct = ContentType.objects.get_for_id(ct_id) for o in ct.model_class().objects.select_related().filter( id__in=items_.keys()).all(): (gfk_name, item_id) = items_[o.id] setattr(item_map[item_id], gfk_name, o) return qs