diff --git a/django/db/models/base.py b/django/db/models/base.py
index bf9854d..b0a5094 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -407,6 +407,20 @@ class Model(six.with_metaclass(ModelBase, object)):
         super(Model, self).__init__()
         signals.post_init.send(sender=self.__class__, instance=self)
 
+    @classmethod
+    def from_db(cls, using, values, field_names, init_with_args, can_fast_init):
+        if can_fast_init:
+            new = Empty()
+            new.__class__ = cls
+            new.__dict__ = dict(zip(field_names, values))
+            new._state = ModelState()
+        elif init_with_args:
+            new = cls(*values)
+        else:
+            new = cls(**dict(zip(field_names, values)))
+        new._state.adding, new._state.db = False, using
+        return new
+
     def __repr__(self):
         try:
             u = six.text_type(self)
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 45b32b0..5e82c4c 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -12,6 +12,7 @@ from django.db.models.loading import get_models, app_cache_ready
 from django.utils import six
 from django.utils.datastructures import SortedDict
 from django.utils.encoding import force_text, smart_text, python_2_unicode_compatible
+from django.utils.functional import cached_property
 from django.utils.translation import activate, deactivate_all, get_language, string_concat
 
 # Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
@@ -352,6 +353,10 @@ class Options(object):
         # the main example). Trim them.
         return [val for val in names if not val.endswith('+')]
 
+    @cached_property
+    def attnames(self):
+        return [f.attname for f in self.fields]
+
     def init_name_map(self):
         """
         Initialises the field name -> field object mapping.
@@ -520,3 +525,25 @@ class Options(object):
                 # of the chain to the ancestor is that parent
                 # links
                 return self.parents[parent] or parent_link
+
+    def can_fast_init(self, attnames):
+        """
+        Checks if it is possible to skip Model.__init__ and instead use
+        a fast-path for the init.
+
+        The skip can be done if the model doesn't override `__int__`, doesn't
+        have pre or post_init signals and doesn't have setters for the
+        attnames (either through __setattr__ or descriptors).
+        """
+        from django.db.models import Model, signals
+        has_descriptors = False
+        for attname in attnames:
+            if attname in self.model.__dict__ and hasattr(self.model.__dict__[attname], '__set__'):
+                has_descriptors = True
+                break
+        return (
+            self.model.__init__ == Model.__init__ and
+            self.model.__setattr__ == object.__setattr__ and not
+            signals.pre_init.has_listeners(self.model) and not
+            signals.post_init.has_listeners(self.model) and not
+            has_descriptors)
diff --git a/django/db/models/query.py b/django/db/models/query.py
index f56d5d2..d6b781e 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -15,8 +15,8 @@ from django.db.models.query_utils import (Q, select_related_descend,
     deferred_class_factory, InvalidQuery)
 from django.db.models.deletion import Collector
 from django.db.models import sql
-from django.utils.functional import partition
 from django.utils import six
+from django.utils.functional import partition
 
 # Used to control how many objects are worked with at once in some cases (e.g.
 # when deleting objects).
@@ -288,12 +288,17 @@ class QuerySet(object):
                 else:
                     init_list.append(field.attname)
             model_cls = deferred_class_factory(self.model, skip)
+        else:
+            model_cls = self.model
+            init_list = [f.attname for f in model_cls._meta.fields]
 
-        # Cache db, model and known_related_object outside the loop
+        # Pre-calculate & cache everything possible outside the loop.
         db = self.db
         model = self.model
         kro_attname, kro_instance = self._known_related_object or (None, None)
         compiler = self.query.get_compiler(using=db)
+        can_fast_init = model._meta.can_fast_init(init_list)
+        init_with_kwargs = not bool(skip)
         if fill_cache:
             klass_info = get_klass_info(model, max_depth=max_depth,
                                         requested=requested, only_load=only_load)
@@ -302,17 +307,8 @@ class QuerySet(object):
                 obj, _ = get_cached_row(row, index_start, db, klass_info,
                                         offset=len(aggregate_select))
             else:
-                # Omit aggregates in object creation.
-                row_data = row[index_start:aggregate_start]
-                if skip:
-                    obj = model_cls(**dict(zip(init_list, row_data)))
-                else:
-                    obj = model(*row_data)
-
-                # Store the source database of the object
-                obj._state.db = db
-                # This object came from the database; it's not being added.
-                obj._state.adding = False
+                obj = model_cls.from_db(db, row[index_start:aggregate_start], init_list,
+                                        init_with_kwargs, can_fast_init)
 
             if extra_select:
                 for i, k in enumerate(extra_select):
@@ -1411,7 +1407,8 @@ def get_klass_info(klass, max_depth=0, cur_depth=0, requested=None,
     else:
         pk_idx = klass._meta.pk_index()
 
-    return klass, field_names, field_count, related_fields, reverse_related_fields, pk_idx
+    can_fast_init = klass._meta.can_fast_init(field_names)
+    return klass, field_names, field_count, related_fields, reverse_related_fields, pk_idx, can_fast_init
 
 
 def get_cached_row(row, index_start, using,  klass_info, offset=0,
@@ -1437,26 +1434,21 @@ def get_cached_row(row, index_start, using,  klass_info, offset=0,
     """
     if klass_info is None:
         return None
-    klass, field_names, field_count, related_fields, reverse_related_fields, pk_idx = klass_info
+    klass, field_names, field_count, related_fields, reverse_related_fields, pk_idx, can_fast_init = klass_info
 
-
-    fields = row[index_start : index_start + field_count]
+    fields = row[index_start: index_start + field_count]
     # If the pk column is None (or the Oracle equivalent ''), then the related
     # object must be non-existent - set the relation to None.
-    if fields[pk_idx] == None or fields[pk_idx] == '':
+    if fields[pk_idx] is None or fields[pk_idx] == '':
         obj = None
     elif field_names:
         fields = list(fields)
         for rel_field, value in parent_data:
             field_names.append(rel_field.attname)
             fields.append(value)
-        obj = klass(**dict(zip(field_names, fields)))
+        obj = klass.from_db(using, fields, field_names, False, can_fast_init)
     else:
-        obj = klass(*fields)
-    # If an object was retrieved, set the database state.
-    if obj:
-        obj._state.db = using
-        obj._state.adding = False
+        obj = klass.from_db(using, fields, klass._meta.attnames, True, can_fast_init)
 
     # Instantiate related fields
     index_end = index_start + field_count + offset
@@ -1529,10 +1521,6 @@ class RawQuerySet(object):
         self.translations = translations or {}
 
     def __iter__(self):
-        # Mapping of attrnames to row column positions. Used for constructing
-        # the model using kwargs, needed when not all model's fields are present
-        # in the query.
-        model_init_field_names = {}
         # A list of tuples of (column name, column position). Used for
         # annotation fields.
         annotation_fields = []
@@ -1548,51 +1536,50 @@ class RawQuerySet(object):
 
         # Find out which columns are model's fields, and which ones should be
         # annotated to the model.
+        attname_to_colpos = {}
         for pos, column in enumerate(self.columns):
             if column in self.model_fields:
-                model_init_field_names[self.model_fields[column].attname] = pos
+                attname_to_colpos[self.model_fields[column].attname] = pos
             else:
                 annotation_fields.append((column, pos))
 
         # Find out which model's fields are not present in the query.
         skip = set()
+        # List of positions of model's fetched fields in the output.
+        field_col_pos = []
+        model_init_attnames = []
         for field in self.model._meta.fields:
-            if field.attname not in model_init_field_names:
+            if field.attname not in attname_to_colpos:
                 skip.add(field.attname)
+            else:
+                # Find out the pos of this field.
+                field_col_pos.append(attname_to_colpos[field.attname])
+                model_init_attnames.append(field.attname)
+
         if skip:
             if self.model._meta.pk.attname in skip:
                 raise InvalidQuery('Raw query must include the primary key')
             model_cls = deferred_class_factory(self.model, skip)
         else:
+            # All model's fields are present in the query.
             model_cls = self.model
-            # All model's fields are present in the query. So, it is possible
-            # to use *args based model instantation. For each field of the model,
-            # record the query column position matching that field.
-            model_init_field_pos = []
-            for field in self.model._meta.fields:
-                model_init_field_pos.append(model_init_field_names[field.attname])
         if need_resolv_columns:
             fields = [self.model_fields.get(c, None) for c in self.columns]
+        can_fast_init = model_cls._meta.can_fast_init(model_init_attnames)
+        init_with_args = not bool(skip)
         # Begin looping through the query values.
         for values in query:
             if need_resolv_columns:
                 values = compiler.resolve_columns(values, fields)
             # Associate fields to values
-            if skip:
-                model_init_kwargs = {}
-                for attname, pos in six.iteritems(model_init_field_names):
-                    model_init_kwargs[attname] = values[pos]
-                instance = model_cls(**model_init_kwargs)
-            else:
-                model_init_args = [values[pos] for pos in model_init_field_pos]
-                instance = model_cls(*model_init_args)
+            model_init_args = [values[pos] for pos in field_col_pos]
+            instance = model_cls.from_db(
+                db, model_init_args, model_init_attnames,
+                init_with_args, can_fast_init)
             if annotation_fields:
                 for column, pos in annotation_fields:
                     setattr(instance, column, values[pos])
 
-            instance._state.db = db
-            instance._state.adding = False
-
             yield instance
 
     def __repr__(self):
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index c1a690a..a2b2814 100644
--- a/django/db/models/query_utils.py
+++ b/django/db/models/query_utils.py
@@ -166,12 +166,16 @@ def select_related_descend(field, restricted, requested, load_fields, reverse=Fa
 # This function is needed because data descriptors must be defined on a class
 # object, not an instance, to have any effect.
 
+_cls_cache = {}
 def deferred_class_factory(model, attrs):
     """
     Returns a class object that is a copy of "model" with the specified "attrs"
     being replaced with DeferredAttribute objects. The "pk_value" ties the
     deferred attributes to a particular instance of the model.
     """
+    cache_key = model, frozenset(attrs)
+    if cache_key in _cls_cache:
+        return _cls_cache[cache_key]
     class Meta:
         proxy = True
         app_label = model._meta.app_label
@@ -188,7 +192,9 @@ def deferred_class_factory(model, attrs):
     overrides["Meta"] = Meta
     overrides["__module__"] = model.__module__
     overrides["_deferred"] = True
-    return type(str(name), (model,), overrides)
+    ret = type(str(name), (model,), overrides)
+    _cls_cache[cache_key] = ret
+    return ret
 
 # The above function is also used to unpickle model instances with deferred
 # fields.
diff --git a/tests/regressiontests/select_related_regress/models.py b/tests/regressiontests/select_related_regress/models.py
index a291a54..0d4ebbb 100644
--- a/tests/regressiontests/select_related_regress/models.py
+++ b/tests/regressiontests/select_related_regress/models.py
@@ -94,3 +94,21 @@ class Item(models.Model):
 
     def __str__(self):
         return self.name
+
+# Models for fast-path init tests
+class Rel(models.Model):
+    name = models.CharField(max_length=10)
+    type = models.CharField(max_length=10)
+
+    def __init__(self, *args, **kwargs):
+        # Override init so that fast-path is skipped
+        super(Rel, self).__init__(*args, **kwargs)
+
+class Base(models.Model):
+    name = models.CharField(max_length=10)
+    type = models.CharField(max_length=10)
+    rel = models.ForeignKey(Rel)
+
+    def __init__(self, *args, **kwargs):
+        # Override init so that fast-path is skipped
+        super(Base, self).__init__(*args, **kwargs)
diff --git a/tests/regressiontests/select_related_regress/tests.py b/tests/regressiontests/select_related_regress/tests.py
index 7f93a1c..096f062 100644
--- a/tests/regressiontests/select_related_regress/tests.py
+++ b/tests/regressiontests/select_related_regress/tests.py
@@ -5,7 +5,7 @@ from django.utils import six
 
 from .models import (Building, Child, Device, Port, Item, Country, Connection,
     ClientStatus, State, Client, SpecialClient, TUser, Person, Student,
-    Organizer, Class, Enrollment)
+    Organizer, Class, Enrollment, Rel, Base)
 
 
 class SelectRelatedRegressTests(TestCase):
@@ -139,3 +139,15 @@ class SelectRelatedRegressTests(TestCase):
         self.assertEqual(troy.name, 'Troy Buswell')
         self.assertEqual(troy.value, 42)
         self.assertEqual(troy.state.name, 'Western Australia')
+
+    def test_no_fastinit_select_related(self):
+        r1 = Rel.objects.create(name='r1', type='r1t1')
+        b1 = Base.objects.create(name='b1', type='b1t1', rel=r1)
+        qs = Base.objects.select_related('rel').only('type', 'rel__type')
+        self.assertEqual(len(qs), 1)
+        with self.assertNumQueries(0):
+            self.assertEqual(qs[0].type, b1.type)
+            self.assertEqual(qs[0].rel.type, r1.type)
+        with self.assertNumQueries(2):
+            self.assertEqual(qs[0].name, b1.name)
+            self.assertEqual(qs[0].rel.name, r1.name)
