from django.db.models import Manager from django.db.models.query import QuerySet from django.db.models.fields.related import SingleRelatedObjectDescriptor class _DerivedNamesMixin(object): def _get_derived_names(self): return [k for k, v in self.model.__dict__.iteritems() if isinstance(v, SingleRelatedObjectDescriptor) and issubclass(v.related.model, self.model)] class DerivedQuerySet(_DerivedNamesMixin, QuerySet): def iterator(self): prefetched = super(DerivedQuerySet, self).select_related( *self._get_derived_names()) for obj in super(DerivedQuerySet, prefetched).iterator(): yield self.__get_derived(obj) def __get_derived(self, instance): from django.core.exceptions import ObjectDoesNotExist for derived_name in self._get_derived_names(): try: return getattr(instance, derived_name) except ObjectDoesNotExist: pass return instance class DerivedManager(_DerivedNamesMixin, models.Manager): def get_query_set(self, *args, **kwargs): return DerivedQuerySet(self.model, using=self._db).select_related( *self._get_derived_names())