summaryrefslogtreecommitdiff
path: root/django/db/models/sql
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2011-09-09 19:22:28 +0000
committerAlex Gaynor <alex.gaynor@gmail.com>2011-09-09 19:22:28 +0000
commit7deb25b8dd5aa1ed02b5e30cbc67cd1fb0c3d6e6 (patch)
tree09ed3208f309d71b5710b8287bccdb92ddf40c58 /django/db/models/sql
parente55bbf4c3c42b17d52c21bc3a460b4981fdc190c (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.py52
-rw-r--r--django/db/models/sql/query.py3
-rw-r--r--django/db/models/sql/subqueries.py26
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):
"""