mirror of https://github.com/django/django.git
Fixed #14700 - speed up RawQuerySet iterator.
This moves constant work out of the loop, and uses the much faster *args based model instantiation where possible, to produce very large speed ups. Thanks to akaariai for the report and patch. git-svn-id: http://code.djangoproject.com/svn/django/trunk@14692 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
3abcd9d3e2
commit
073412b411
|
@ -1299,8 +1299,67 @@ class RawQuerySet(object):
|
|||
self.translations = translations or {}
|
||||
|
||||
def __iter__(self):
|
||||
for row in self.query:
|
||||
yield self.transform_results(row)
|
||||
# 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 = []
|
||||
|
||||
# Cache some things for performance reasons outside the loop.
|
||||
db = self.db
|
||||
compiler = connections[db].ops.compiler('SQLCompiler')(self.query, connections[db], db)
|
||||
need_resolv_columns = hasattr(compiler, 'resolve_columns')
|
||||
|
||||
# Find out which columns are model's fields, and which ones should be
|
||||
# annotated to the model.
|
||||
for pos, column in enumerate(self.columns):
|
||||
if column in self.model_fields:
|
||||
model_init_field_names[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()
|
||||
for field in self.model._meta.fields:
|
||||
if field.attname not in model_init_field_names:
|
||||
skip.add(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:
|
||||
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]
|
||||
# Begin looping through the query values.
|
||||
for values in self.query:
|
||||
if need_resolv_columns:
|
||||
values = compiler.resolve_columns(values, fields)
|
||||
# Associate fields to values
|
||||
if skip:
|
||||
model_init_kwargs = {}
|
||||
for attname, pos in model_init_field_names.iteritems():
|
||||
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)
|
||||
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):
|
||||
return "<RawQuerySet: %r>" % (self.raw_query % self.params)
|
||||
|
@ -1355,50 +1414,6 @@ class RawQuerySet(object):
|
|||
self._model_fields[converter(column)] = field
|
||||
return self._model_fields
|
||||
|
||||
def transform_results(self, values):
|
||||
model_init_kwargs = {}
|
||||
annotations = ()
|
||||
|
||||
# Perform database backend type resolution
|
||||
connection = connections[self.db]
|
||||
compiler = connection.ops.compiler('SQLCompiler')(self.query, connection, self.db)
|
||||
if hasattr(compiler, 'resolve_columns'):
|
||||
fields = [self.model_fields.get(c,None) for c in self.columns]
|
||||
values = compiler.resolve_columns(values, fields)
|
||||
|
||||
# Associate fields to values
|
||||
for pos, value in enumerate(values):
|
||||
column = self.columns[pos]
|
||||
|
||||
# Separate properties from annotations
|
||||
if column in self.model_fields.keys():
|
||||
model_init_kwargs[self.model_fields[column].attname] = value
|
||||
else:
|
||||
annotations += (column, value),
|
||||
|
||||
# Construct model instance and apply annotations
|
||||
skip = set()
|
||||
for field in self.model._meta.fields:
|
||||
if field.attname not in model_init_kwargs.keys():
|
||||
skip.add(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:
|
||||
model_cls = self.model
|
||||
|
||||
instance = model_cls(**model_init_kwargs)
|
||||
|
||||
for field, value in annotations:
|
||||
setattr(instance, field, value)
|
||||
|
||||
instance._state.db = self.query.using
|
||||
instance._state.adding = False
|
||||
|
||||
return instance
|
||||
|
||||
def insert_query(model, values, return_id=False, raw_values=False, using=None):
|
||||
"""
|
||||
Inserts a new record for the given model. This provides an interface to
|
||||
|
|
Loading…
Reference in New Issue