diff options
| author | Alex Gaynor <alex.gaynor@gmail.com> | 2011-09-09 19:22:28 +0000 |
|---|---|---|
| committer | Alex Gaynor <alex.gaynor@gmail.com> | 2011-09-09 19:22:28 +0000 |
| commit | 7deb25b8dd5aa1ed02b5e30cbc67cd1fb0c3d6e6 (patch) | |
| tree | 09ed3208f309d71b5710b8287bccdb92ddf40c58 /django/db/models/sql | |
| parent | e55bbf4c3c42b17d52c21bc3a460b4981fdc190c (diff) | |
Fixed #7596. Added Model.objects.bulk_create, and make use of it in several places. This provides a performance benefit when inserting multiple objects. THanks to Russ for the review, and Simon Meers for the MySQl implementation.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@16739 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/db/models/sql')
| -rw-r--r-- | django/db/models/sql/compiler.py | 52 | ||||
| -rw-r--r-- | django/db/models/sql/query.py | 3 | ||||
| -rw-r--r-- | django/db/models/sql/subqueries.py | 26 |
3 files changed, 56 insertions, 25 deletions
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 05c19f33d5..b8bba4b013 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -1,3 +1,5 @@ +from itertools import izip + from django.core.exceptions import FieldError from django.db import connections from django.db import transaction @@ -9,6 +11,7 @@ from django.db.models.sql.query import (get_proxied_model, get_order_dir, select_related_descend, Query) from django.db.utils import DatabaseError + class SQLCompiler(object): def __init__(self, query, connection, using): self.query = query @@ -794,20 +797,55 @@ class SQLInsertCompiler(SQLCompiler): qn = self.connection.ops.quote_name opts = self.query.model._meta result = ['INSERT INTO %s' % qn(opts.db_table)] - result.append('(%s)' % ', '.join([qn(c) for c in self.query.columns])) - values = [self.placeholder(*v) for v in self.query.values] - result.append('VALUES (%s)' % ', '.join(values)) - params = self.query.params + + has_fields = bool(self.query.fields) + fields = self.query.fields if has_fields else [opts.pk] + result.append('(%s)' % ', '.join([qn(f.column) for f in fields])) + + if has_fields: + params = values = [ + [ + f.get_db_prep_save(getattr(obj, f.attname) if self.query.raw else f.pre_save(obj, True), connection=self.connection) + for f in fields + ] + for obj in self.query.objs + ] + else: + values = [[self.connection.ops.pk_default_value()] for obj in self.query.objs] + params = [[]] + fields = [None] + can_bulk = not any(hasattr(field, "get_placeholder") for field in fields) and not self.return_id + + if can_bulk: + placeholders = [["%s"] * len(fields)] + else: + placeholders = [ + [self.placeholder(field, v) for field, v in izip(fields, val)] + for val in values + ] if self.return_id and self.connection.features.can_return_id_from_insert: + params = values[0] col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column)) + result.append("VALUES (%s)" % ", ".join(placeholders[0])) r_fmt, r_params = self.connection.ops.return_insert_id() result.append(r_fmt % col) - params = params + r_params - return ' '.join(result), params + params += r_params + return [(" ".join(result), tuple(params))] + if can_bulk and self.connection.features.has_bulk_insert: + result.append(self.connection.ops.bulk_insert_sql(fields, len(values))) + return [(" ".join(result), tuple([v for val in values for v in val]))] + else: + return [ + (" ".join(result + ["VALUES (%s)" % ", ".join(p)]), vals) + for p, vals in izip(placeholders, params) + ] def execute_sql(self, return_id=False): + assert not (return_id and len(self.query.objs) != 1) self.return_id = return_id - cursor = super(SQLInsertCompiler, self).execute_sql(None) + cursor = self.connection.cursor() + for sql, params in self.as_sql(): + cursor.execute(sql, params) if not (return_id and cursor): return if self.connection.features.can_return_id_from_insert: diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index b55de3d766..101d4ac7a5 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -8,9 +8,10 @@ all about the internals of models in order to get the information it needs. """ import copy -from django.utils.tree import Node + from django.utils.datastructures import SortedDict from django.utils.encoding import force_unicode +from django.utils.tree import Node from django.db import connections, DEFAULT_DB_ALIAS from django.db.models import signals from django.db.models.fields import FieldDoesNotExist diff --git a/django/db/models/sql/subqueries.py b/django/db/models/sql/subqueries.py index ecde857a9b..1b03647595 100644 --- a/django/db/models/sql/subqueries.py +++ b/django/db/models/sql/subqueries.py @@ -136,20 +136,19 @@ class InsertQuery(Query): def __init__(self, *args, **kwargs): super(InsertQuery, self).__init__(*args, **kwargs) - self.columns = [] - self.values = [] - self.params = () + self.fields = [] + self.objs = [] def clone(self, klass=None, **kwargs): extras = { - 'columns': self.columns[:], - 'values': self.values[:], - 'params': self.params + 'fields': self.fields[:], + 'objs': self.objs[:], + 'raw': self.raw, } extras.update(kwargs) return super(InsertQuery, self).clone(klass, **extras) - def insert_values(self, insert_values, raw_values=False): + def insert_values(self, fields, objs, raw=False): """ Set up the insert query from the 'insert_values' dictionary. The dictionary gives the model field names and their target values. @@ -159,16 +158,9 @@ class InsertQuery(Query): parameters. This provides a way to insert NULL and DEFAULT keywords into the query, for example. """ - placeholders, values = [], [] - for field, val in insert_values: - placeholders.append((field, val)) - self.columns.append(field.column) - values.append(val) - if raw_values: - self.values.extend([(None, v) for v in values]) - else: - self.params += tuple(values) - self.values.extend(placeholders) + self.fields = fields + self.objs = objs + self.raw = raw class DateQuery(Query): """ |
