Index: django/db/models/sql/query.py
===================================================================
--- django/db/models/sql/query.py	(revision 9232)
+++ django/db/models/sql/query.py	(working copy)
@@ -197,6 +197,17 @@
             obj._setup_query()
         return obj
 
+    def get_base_fields(self):
+        """
+        Returns a list of fields on the base object (i.e., not related objects) 
+        that are retrieved by this query. In the result row, these columns are
+        row[len(self.extra_select):len(self.extra_select)+len(self.get_base_fields())]
+        """
+        if self.select_fields:
+            return self.select_fields
+        else:
+            return self.model._meta.fields
+
     def results_iter(self):
         """
         Returns an iterator over the results from executing this query.
@@ -210,10 +221,9 @@
                         # We only set this up here because
                         # related_select_fields isn't populated until
                         # execute_sql() has been called.
-                        if self.select_fields:
-                            fields = self.select_fields + self.related_select_fields
-                        else:
-                            fields = self.model._meta.fields
+                        fields = self.get_base_fields() 
+                        if self.related_select_fields:
+                            fields = fields + self.related_select_fields
                     row = self.resolve_columns(row, fields)
                 yield row
 
@@ -435,90 +445,100 @@
         if self.select:
             for col in self.select:
                 if isinstance(col, (list, tuple)):
-                    r = '%s.%s' % (qn(col[0]), qn(col[1]))
-                    if with_aliases and col[1] in col_aliases:
-                        c_alias = 'Col%d' % len(col_aliases)
-                        result.append('%s AS %s' % (r, c_alias))
-                        aliases.add(c_alias)
-                        col_aliases.add(c_alias)
-                    else:
-                        result.append(r)
-                        aliases.add(r)
-                        col_aliases.add(col[1])
+                    result.append(self._get_column(                            
+                        table_alias=col[0],column_name=col[1],
+                        with_aliases=with_aliases,
+                        col_aliases=col_aliases,
+                        aliases=aliases))
                 else:
                     result.append(col.as_sql(quote_func=qn))
                     if hasattr(col, 'alias'):
                         aliases.add(col.alias)
                         col_aliases.add(col.alias)
         elif self.default_cols:
-            cols, new_aliases = self.get_default_columns(with_aliases,
-                    col_aliases)
-            result.extend(cols)
-            aliases.update(new_aliases)
-        for table, col in self.related_select_cols:
-            r = '%s.%s' % (qn(table), qn(col))
-            if with_aliases and col in col_aliases:
-                c_alias = 'Col%d' % len(col_aliases)
-                result.append('%s AS %s' % (r, c_alias))
-                aliases.add(c_alias)
-                col_aliases.add(c_alias)
-            else:
-                result.append(r)
-                aliases.add(r)
-                col_aliases.add(col)
+            result.extend(self.get_base_columns(with_aliases, col_aliases, aliases))
+        
+        for table, column_name in self.related_select_cols:
+            result.append(self._get_column(table, column_name, 
+                with_aliases, col_aliases, aliases))
 
         self._select_aliases = aliases
         return result
 
-    def get_default_columns(self, with_aliases=False, col_aliases=None,
-            start_alias=None, opts=None, as_pairs=False):
+    def _get_table_alias(self, model, seen, root_pk):
         """
-        Computes the default columns for selecting every field in the base
-        model.
+        Gets a table alias for the given model. Seen is a dict
+        mapping already-used models to table aliases, and may be updated
+        as a side effect.
+        """    
+        try:
+            return seen[model]
+        except KeyError:            
+            alias = self.join((seen[None], model._meta.db_table,
+                    root_pk, model._meta.pk.column))
+            seen[model] = alias        
+            return alias
+            
+    def get_base_columns(self, with_aliases, col_aliases, aliases):
+        """
+        Computes the columns to select on the base model. By default, this includes all 
+        fields. 
 
-        Returns a list of strings, quoted appropriately for use in SQL
-        directly, as well as a set of aliases used in the select statement (if
-        'as_pairs' is True, returns a list of (alias, col_name) pairs instead
-        of strings as the first component and None as the second component).
+        Returns a list of strings, quoted appropriately for use in SQL directly.
+        As a side effect, updates the aliases and col_aliases sets passed in as an argument.
+        """      
+        result = []        
+        opts = self.model._meta
+        root_pk = opts.pk.column
+        seen = {None: self.tables[0]}
+        
+        for field, model in opts.get_fields_with_model():
+            table_alias = self._get_table_alias(model, seen, root_pk)    
+            result.append(self._get_column(table_alias, field.column, 
+                                           with_aliases, col_aliases, aliases))
+                
+        return result
+    
+    def get_related_columns(self, start_alias, opts):
         """
-        result = []
-        if opts is None:
-            opts = self.model._meta
-        if start_alias:
-            table_alias = start_alias
-        else:
-            table_alias = self.tables[0]
+        Computes the default columns for selecting every field for a related 
+        model (described by opts). The related field columns currently cannot be 
+        customized per-query.
+
+        Returns a list of (alias, field) pairs, where alias is a string quoted 
+        appropriately for use in SQL directly.
+        """
+        result = [] 
+        seen = {None: start_alias}
         root_pk = opts.pk.column
-        seen = {None: table_alias}
-        qn = self.quote_name_unless_alias
-        qn2 = self.connection.ops.quote_name
-        aliases = set()
+        
         for field, model in opts.get_fields_with_model():
-            try:
-                alias = seen[model]
-            except KeyError:
-                alias = self.join((table_alias, model._meta.db_table,
-                        root_pk, model._meta.pk.column))
-                seen[model] = alias
-            if as_pairs:
-                result.append((alias, field.column))
-                continue
-            if with_aliases and field.column in col_aliases:
-                c_alias = 'Col%d' % len(col_aliases)
-                result.append('%s.%s AS %s' % (qn(alias),
-                    qn2(field.column), c_alias))
-                col_aliases.add(c_alias)
-                aliases.add(c_alias)
-            else:
-                r = '%s.%s' % (qn(alias), qn2(field.column))
-                result.append(r)
-                aliases.add(r)
-                if with_aliases:
-                    col_aliases.add(field.column)
-        if as_pairs:
-            return result, None
-        return result, aliases
+            result.append((self._get_table_alias(model, seen, root_pk), field.column))
+                
+        return result
 
+    def _get_column(self, table_alias, column_name, with_aliases, col_aliases, aliases):        
+        """
+        Returns a string for a SQL select statement, containing a column name 
+        as well as a column alias if necessary.
+        
+        The col_aliases and aliases sets are modified as a side effect.
+        """                
+        r = '%s.%s' % (self.quote_name_unless_alias(table_alias), self.connection.ops.quote_name(column_name))        
+        if with_aliases and column_name in col_aliases:
+            c_alias = 'Col%d' % len(col_aliases)
+            
+            col_aliases.add(c_alias)
+            aliases.add(c_alias)
+            
+            return '%s AS %s' % (r, c_alias)
+        else:
+            aliases.add(r)
+            if with_aliases:
+                col_aliases.add(column_name)    
+                
+            return r
+
     def get_from_clause(self):
         """
         Returns a list of strings that are joined together to go after the
@@ -1046,8 +1066,8 @@
                     f.rel.get_related_field().column),
                     exclusions=used.union(avoid), promote=promote)
             used.add(alias)
-            self.related_select_cols.extend(self.get_default_columns(
-                start_alias=alias, opts=f.rel.to._meta, as_pairs=True)[0])
+            new_related_cols = self.get_related_columns(start_alias=alias, opts=f.rel.to._meta)            
+            self.related_select_cols.extend(new_related_cols)
             self.related_select_fields.extend(f.rel.to._meta.fields)
             if restricted:
                 next = requested.get(f.name, {})
@@ -1529,6 +1549,10 @@
                         True)
                 final_alias = joins[-1]
                 col = target.column
+                
+                # When name ends with something like fk_id, setup_joins
+                # will add an unnecessary join on the fk. In this case, we undo the
+                # last join, and just select fk_id on the previous model.
                 if len(joins) > 1:
                     join = self.alias_map[final_alias]
                     if col == join[RHS_JOIN_COL]:
Index: django/db/models/base.py
===================================================================
--- django/db/models/base.py	(revision 9232)
+++ django/db/models/base.py	(working copy)
@@ -265,6 +265,26 @@
                 raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
         signals.post_init.send(sender=self.__class__, instance=self)
 
+    @classmethod
+    def _new_from_select_values(cls, vals, fields):    
+        """
+        Creates a new instance of this model class. vals is a list of columns 
+        (e.g., as retrieved by a select query), and fields is a parallel list
+        of corresponding field objects.
+        
+        The class will be instantiated as if calling __init__ with kwargs
+        fields[i]=vals[i] for all i.
+        
+        However, it may call __init__ using positional arguments when all 
+        fields are provided.
+        """    
+
+        # we use pointer-equality to make the test faster in the normal case    
+        if fields is cls._meta.fields: 
+            return cls(*vals) # the comments for __init__ indicate this is faster 
+        else:            
+            return cls(**dict(zip([f.attname for f in fields], vals)))                 
+
     def __repr__(self):
         return smart_str(u'<%s: %s>' % (self.__class__.__name__, unicode(self)))
 
Index: django/db/models/query.py
===================================================================
--- django/db/models/query.py	(revision 9232)
+++ django/db/models/query.py	(working copy)
@@ -272,12 +272,16 @@
         max_depth = self.query.max_depth
         extra_select = self.query.extra_select.keys()
         index_start = len(extra_select)
+        
+        fields = self.query.get_base_fields()  
+        model = self.model
+        
         for row in self.query.results_iter():
             if fill_cache:
-                obj, _ = get_cached_row(self.model, row, index_start,
-                        max_depth, requested=requested)
+                obj, _ = get_cached_row(model, row, index_start,
+                        max_depth, requested=requested, fields=fields)
             else:
-                obj = self.model(*row[index_start:])
+                obj = model._new_from_select_values(row[index_start:], fields)
             for i, k in enumerate(extra_select):
                 setattr(obj, k, row[i])
             yield obj
@@ -683,7 +687,6 @@
             field_names = [f.attname for f in self.model._meta.fields]
 
         self.query.add_fields(field_names, False)
-        self.query.default_cols = False
         self.field_names = field_names
 
     def _clone(self, klass=None, setup=False, **kwargs):
@@ -788,7 +791,7 @@
 
 
 def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0,
-                   requested=None):
+                   requested=None, fields=None):
     """
     Helper function that recursively returns an object with the specified
     related attributes already populated.
@@ -797,14 +800,17 @@
         # We've recursed deeply enough; stop now.
         return None
 
+    if fields is None:
+        fields = klass._meta.fields
+
     restricted = requested is not None
-    index_end = index_start + len(klass._meta.fields)
-    fields = row[index_start:index_end]
-    if not [x for x in fields if x is not None]:
+    index_end = index_start + len(fields)
+    values = row[index_start:index_end]
+    if not [x for x in values if x is not None]:
         # If we only have a list of Nones, there was not related object.
         obj = None
     else:
-        obj = klass(*fields)
+        obj = klass._new_from_select_values(values, fields)
     for f in klass._meta.fields:
         if not select_related_descend(f, restricted, requested):
             continue
Index: django/contrib/gis/db/models/sql/query.py
===================================================================
--- django/contrib/gis/db/models/sql/query.py	(revision 9232)
+++ django/contrib/gis/db/models/sql/query.py	(working copy)
@@ -66,93 +66,71 @@
             # This loop customized for GeoQuery.
             for col, field in izip(self.select, self.select_fields):
                 if isinstance(col, (list, tuple)):
-                    r = self.get_field_select(field, col[0])
-                    if with_aliases and col[1] in col_aliases:
-                        c_alias = 'Col%d' % len(col_aliases)
-                        result.append('%s AS %s' % (r, c_alias))
-                        aliases.add(c_alias)
-                        col_aliases.add(c_alias)
-                    else:
-                        result.append(r)
-                        aliases.add(r)
-                        col_aliases.add(col[1])
+                    result.append(self._get_column_with_field(
+                        table_alias=col[0],column_name=col[1],field=field,
+                        with_aliases=with_aliases,
+                        col_aliases=col_aliases, aliases=aliases))
                 else:
                     result.append(col.as_sql(quote_func=qn))
                     if hasattr(col, 'alias'):
                         aliases.add(col.alias)
                         col_aliases.add(col.alias)
         elif self.default_cols:
-            cols, new_aliases = self.get_default_columns(with_aliases,
-                    col_aliases)
-            result.extend(cols)
-            aliases.update(new_aliases)
+            result.extend(self.get_base_columns(with_aliases, col_aliases, aliases))
         # This loop customized for GeoQuery.
         if not self.aggregate:
             for (table, col), field in izip(self.related_select_cols, self.related_select_fields):
-                r = self.get_field_select(field, table)
-                if with_aliases and col in col_aliases:
-                    c_alias = 'Col%d' % len(col_aliases)
-                    result.append('%s AS %s' % (r, c_alias))
-                    aliases.add(c_alias)
-                    col_aliases.add(c_alias)
-                else:
-                    result.append(r)
-                    aliases.add(r)
-                    col_aliases.add(col)
+                result.append(self._get_column_with_field(table, col, field,
+                                                          with_aliases, col_aliases, aliases))
 
         self._select_aliases = aliases
         return result
 
-    def get_default_columns(self, with_aliases=False, col_aliases=None,
-                            start_alias=None, opts=None, as_pairs=False):
+    def _get_column_with_field(self, table_alias, column_name, field, with_aliases, col_aliases, aliases):        
         """
-        Computes the default columns for selecting every field in the base
-        model.
+        Returns a string for a SQL select statement, containing a column name 
+        as well as a column alias if necessary.
+        
+        The col_aliases and aliases sets are modified as a side effect.
+        """                
+        r = self.get_field_select(field, table_alias)
+        
+        if with_aliases and column_name in col_aliases:
+            c_alias = 'Col%d' % len(col_aliases)
+            
+            col_aliases.add(c_alias)
+            aliases.add(c_alias)
+            
+            return '%s AS %s' % (r, c_alias)
+        else:
+            aliases.add(r)
+            if with_aliases:
+                col_aliases.add(column_name)    
+                
+            return r
 
-        Returns a list of strings, quoted appropriately for use in SQL
-        directly, as well as a set of aliases used in the select statement.
-
-        This routine is overridden from Query to handle customized selection of 
-        geometry columns.
+    def get_base_columns(self, with_aliases, col_aliases, aliases):
         """
-        result = []
-        if opts is None:
-            opts = self.model._meta
-        if start_alias:
-            table_alias = start_alias
-        else:
-            table_alias = self.tables[0]
-        root_pk = self.model._meta.pk.column
-        seen = {None: table_alias}
-        aliases = set()
+        Computes the columns to select on the base model. By default, this includes all 
+        fields. 
+
+        Returns a list of strings, quoted appropriately for use in SQL directly.
+        As a side effect, updates the aliases and col_aliases sets passed in as an argument.
+        
+        This routine is overridden from Query to handle customized selection of  
+        geometry columns. 
+        """      
+        result = []        
+        opts = self.model._meta
+        root_pk = opts.pk.column
+        seen = {None: self.tables[0]}
+        
         for field, model in opts.get_fields_with_model():
-            try:
-                alias = seen[model]
-            except KeyError:
-                alias = self.join((table_alias, model._meta.db_table,
-                        root_pk, model._meta.pk.column))
-                seen[model] = alias
-            if as_pairs:
-                result.append((alias, field.column))
-                continue
-            # This part of the function is customized for GeoQuery. We
-            # see if there was any custom selection specified in the
-            # dictionary, and set up the selection format appropriately.
-            field_sel = self.get_field_select(field, alias)
-            if with_aliases and field.column in col_aliases:
-                c_alias = 'Col%d' % len(col_aliases)
-                result.append('%s AS %s' % (field_sel, c_alias))
-                col_aliases.add(c_alias)
-                aliases.add(c_alias)
-            else:
-                r = field_sel
-                result.append(r)
-                aliases.add(r)
-                if with_aliases:
-                    col_aliases.add(field.column)
-        if as_pairs:
-            return result, None
-        return result, aliases
+            table_alias = self._get_table_alias(model, seen, root_pk)    
+            result.append(self._get_column_with_field(table_alias, field.column, field, 
+                                           with_aliases, col_aliases, aliases))
+                
+        return result    
 
     def get_ordering(self):
         """
