|
@@ -1810,23 +1810,65 @@ class SQLInsertCompiler(SQLCompiler):
|
|
|
on_conflict=self.query.on_conflict,
|
|
|
)
|
|
|
result = ["%s %s" % (insert_statement, qn(opts.db_table))]
|
|
|
- fields = self.query.fields or [opts.pk]
|
|
|
- result.append("(%s)" % ", ".join(qn(f.column) for f in fields))
|
|
|
|
|
|
- if self.query.fields:
|
|
|
- value_rows = [
|
|
|
- [
|
|
|
- self.prepare_value(field, self.pre_save_val(field, obj))
|
|
|
- for field in fields
|
|
|
+ if fields := list(self.query.fields):
|
|
|
+ from django.db.models.expressions import DatabaseDefault
|
|
|
+
|
|
|
+ supports_default_keyword_in_bulk_insert = (
|
|
|
+ self.connection.features.supports_default_keyword_in_bulk_insert
|
|
|
+ )
|
|
|
+ value_cols = []
|
|
|
+ for field in list(fields):
|
|
|
+ field_prepare = partial(self.prepare_value, field)
|
|
|
+ field_pre_save = partial(self.pre_save_val, field)
|
|
|
+ field_values = [
|
|
|
+ field_prepare(field_pre_save(obj)) for obj in self.query.objs
|
|
|
]
|
|
|
- for obj in self.query.objs
|
|
|
- ]
|
|
|
+
|
|
|
+ if not field.has_db_default():
|
|
|
+ value_cols.append(field_values)
|
|
|
+ continue
|
|
|
+
|
|
|
+ # If all values are DEFAULT don't include the field and its
|
|
|
+ # values in the query as they are redundant and could prevent
|
|
|
+ # optimizations. This cannot be done if we're dealing with the
|
|
|
+ # last field as INSERT statements require at least one.
|
|
|
+ if len(fields) > 1 and all(
|
|
|
+ isinstance(value, DatabaseDefault) for value in field_values
|
|
|
+ ):
|
|
|
+ fields.remove(field)
|
|
|
+ continue
|
|
|
+
|
|
|
+ if supports_default_keyword_in_bulk_insert:
|
|
|
+ value_cols.append(field_values)
|
|
|
+ continue
|
|
|
+
|
|
|
+ # If the field cannot be excluded from the INSERT for the
|
|
|
+ # reasons listed above and the backend doesn't support the
|
|
|
+ # DEFAULT keyword each values must be expanded into their
|
|
|
+ # underlying expressions.
|
|
|
+ prepared_db_default = field_prepare(field.db_default)
|
|
|
+ field_values = [
|
|
|
+ (
|
|
|
+ prepared_db_default
|
|
|
+ if isinstance(value, DatabaseDefault)
|
|
|
+ else value
|
|
|
+ )
|
|
|
+ for value in field_values
|
|
|
+ ]
|
|
|
+ value_cols.append(field_values)
|
|
|
+ value_rows = list(zip(*value_cols))
|
|
|
+ result.append("(%s)" % ", ".join(qn(f.column) for f in fields))
|
|
|
else:
|
|
|
- # An empty object.
|
|
|
+ # No fields were specified but an INSERT statement must include at
|
|
|
+ # least one column. This can only happen when the model's primary
|
|
|
+ # key is composed of a single auto-field so default to including it
|
|
|
+ # as a placeholder to generate a valid INSERT statement.
|
|
|
value_rows = [
|
|
|
[self.connection.ops.pk_default_value()] for _ in self.query.objs
|
|
|
]
|
|
|
fields = [None]
|
|
|
+ result.append("(%s)" % qn(opts.pk.column))
|
|
|
|
|
|
# Currently the backends just accept values when generating bulk
|
|
|
# queries and generate their own placeholders. Doing that isn't
|