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 {}
|
self.translations = translations or {}
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for row in self.query:
|
# Mapping of attrnames to row column positions. Used for constructing
|
||||||
yield self.transform_results(row)
|
# 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):
|
def __repr__(self):
|
||||||
return "<RawQuerySet: %r>" % (self.raw_query % self.params)
|
return "<RawQuerySet: %r>" % (self.raw_query % self.params)
|
||||||
|
@ -1355,50 +1414,6 @@ class RawQuerySet(object):
|
||||||
self._model_fields[converter(column)] = field
|
self._model_fields[converter(column)] = field
|
||||||
return self._model_fields
|
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):
|
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
|
Inserts a new record for the given model. This provides an interface to
|
||||||
|
|
Loading…
Reference in New Issue