Refs #29444 -- Renamed DatabaseFeatures.can_return_id* to be generic for other columns.

This commit is contained in:
Johannes Hoppe 2019-01-30 15:31:56 -05:00 committed by Tim Graham
parent 16a5a2a2c8
commit b131f9c79f
8 changed files with 22 additions and 18 deletions

View File

@ -22,8 +22,8 @@ class BaseDatabaseFeatures:
supports_partially_nullable_unique_constraints = True supports_partially_nullable_unique_constraints = True
can_use_chunked_reads = True can_use_chunked_reads = True
can_return_id_from_insert = False can_return_columns_from_insert = False
can_return_ids_from_bulk_insert = False can_return_rows_from_bulk_insert = False
has_bulk_insert = True has_bulk_insert = True
uses_savepoints = True uses_savepoints = True
can_release_savepoints = False can_release_savepoints = False

View File

@ -202,7 +202,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
use_returning_into = self.settings_dict["OPTIONS"].get('use_returning_into', True) use_returning_into = self.settings_dict["OPTIONS"].get('use_returning_into', True)
self.features.can_return_id_from_insert = use_returning_into self.features.can_return_columns_from_insert = use_returning_into
def _dsn(self): def _dsn(self):
settings_dict = self.settings_dict settings_dict = self.settings_dict

View File

@ -10,7 +10,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
has_select_for_update_skip_locked = True has_select_for_update_skip_locked = True
has_select_for_update_of = True has_select_for_update_of = True
select_for_update_of_column = True select_for_update_of_column = True
can_return_id_from_insert = True can_return_columns_from_insert = True
can_introspect_autofield = True can_introspect_autofield = True
supports_subqueries_in_group_by = False supports_subqueries_in_group_by = False
supports_transactions = True supports_transactions = True

View File

@ -7,8 +7,8 @@ from django.utils.functional import cached_property
class DatabaseFeatures(BaseDatabaseFeatures): class DatabaseFeatures(BaseDatabaseFeatures):
allows_group_by_selected_pks = True allows_group_by_selected_pks = True
can_return_id_from_insert = True can_return_columns_from_insert = True
can_return_ids_from_bulk_insert = True can_return_rows_from_bulk_insert = True
has_real_datatype = True has_real_datatype = True
has_native_uuid_field = True has_native_uuid_field = True
has_native_duration_field = True has_native_duration_field = True

View File

@ -431,11 +431,11 @@ class QuerySet:
Insert each of the instances into the database. Do *not* call Insert each of the instances into the database. Do *not* call
save() on each of the instances, do not send any pre/post_save save() on each of the instances, do not send any pre/post_save
signals, and do not set the primary key attribute if it is an signals, and do not set the primary key attribute if it is an
autoincrement field (except if features.can_return_ids_from_bulk_insert=True). autoincrement field (except if features.can_return_rows_from_bulk_insert=True).
Multi-table models are not supported. Multi-table models are not supported.
""" """
# When you bulk insert you don't get the primary keys back (if it's an # When you bulk insert you don't get the primary keys back (if it's an
# autoincrement, except if can_return_ids_from_bulk_insert=True), so # autoincrement, except if can_return_rows_from_bulk_insert=True), so
# you can't insert into the child tables which references this. There # you can't insert into the child tables which references this. There
# are two workarounds: # are two workarounds:
# 1) This could be implemented if you didn't have an autoincrement pk # 1) This could be implemented if you didn't have an autoincrement pk
@ -471,7 +471,7 @@ class QuerySet:
if objs_without_pk: if objs_without_pk:
fields = [f for f in fields if not isinstance(f, AutoField)] fields = [f for f in fields if not isinstance(f, AutoField)]
ids = self._batched_insert(objs_without_pk, fields, batch_size, ignore_conflicts=ignore_conflicts) ids = self._batched_insert(objs_without_pk, fields, batch_size, ignore_conflicts=ignore_conflicts)
if connection.features.can_return_ids_from_bulk_insert and not ignore_conflicts: if connection.features.can_return_rows_from_bulk_insert and not ignore_conflicts:
assert len(ids) == len(objs_without_pk) assert len(ids) == len(objs_without_pk)
for obj_without_pk, pk in zip(objs_without_pk, ids): for obj_without_pk, pk in zip(objs_without_pk, ids):
obj_without_pk.pk = pk obj_without_pk.pk = pk
@ -1185,7 +1185,7 @@ class QuerySet:
ops = connections[self.db].ops ops = connections[self.db].ops
batch_size = (batch_size or max(ops.bulk_batch_size(fields, objs), 1)) batch_size = (batch_size or max(ops.bulk_batch_size(fields, objs), 1))
inserted_ids = [] inserted_ids = []
bulk_return = connections[self.db].features.can_return_ids_from_bulk_insert bulk_return = connections[self.db].features.can_return_rows_from_bulk_insert
for item in [objs[i:i + batch_size] for i in range(0, len(objs), batch_size)]: for item in [objs[i:i + batch_size] for i in range(0, len(objs), batch_size)]:
if bulk_return and not ignore_conflicts: if bulk_return and not ignore_conflicts:
inserted_id = self._insert( inserted_id = self._insert(

View File

@ -1273,8 +1273,8 @@ class SQLInsertCompiler(SQLCompiler):
ignore_conflicts_suffix_sql = self.connection.ops.ignore_conflicts_suffix_sql( ignore_conflicts_suffix_sql = self.connection.ops.ignore_conflicts_suffix_sql(
ignore_conflicts=self.query.ignore_conflicts ignore_conflicts=self.query.ignore_conflicts
) )
if self.return_id and self.connection.features.can_return_id_from_insert: if self.return_id and self.connection.features.can_return_columns_from_insert:
if self.connection.features.can_return_ids_from_bulk_insert: if self.connection.features.can_return_rows_from_bulk_insert:
result.append(self.connection.ops.bulk_insert_sql(fields, placeholder_rows)) result.append(self.connection.ops.bulk_insert_sql(fields, placeholder_rows))
params = param_rows params = param_rows
else: else:
@ -1307,7 +1307,7 @@ class SQLInsertCompiler(SQLCompiler):
def execute_sql(self, return_id=False): def execute_sql(self, return_id=False):
assert not ( assert not (
return_id and len(self.query.objs) != 1 and return_id and len(self.query.objs) != 1 and
not self.connection.features.can_return_ids_from_bulk_insert not self.connection.features.can_return_rows_from_bulk_insert
) )
self.return_id = return_id self.return_id = return_id
with self.connection.cursor() as cursor: with self.connection.cursor() as cursor:
@ -1315,9 +1315,9 @@ class SQLInsertCompiler(SQLCompiler):
cursor.execute(sql, params) cursor.execute(sql, params)
if not return_id: if not return_id:
return return
if self.connection.features.can_return_ids_from_bulk_insert and len(self.query.objs) > 1: if self.connection.features.can_return_rows_from_bulk_insert and len(self.query.objs) > 1:
return self.connection.ops.fetch_returned_insert_ids(cursor) return self.connection.ops.fetch_returned_insert_ids(cursor)
if self.connection.features.can_return_id_from_insert: if self.connection.features.can_return_columns_from_insert:
assert len(self.query.objs) == 1 assert len(self.query.objs) == 1
return self.connection.ops.fetch_returned_insert_id(cursor) return self.connection.ops.fetch_returned_insert_id(cursor)
return self.connection.ops.last_insert_id( return self.connection.ops.last_insert_id(

View File

@ -218,6 +218,10 @@ backends.
field, add ``SchemaEditor.sql_create_column_inline_fk`` with the appropriate field, add ``SchemaEditor.sql_create_column_inline_fk`` with the appropriate
SQL; otherwise, set ``DatabaseFeatures.can_create_inline_fk = False``. SQL; otherwise, set ``DatabaseFeatures.can_create_inline_fk = False``.
* ``DatabaseFeatures.can_return_id_from_insert`` and
``can_return_ids_from_bulk_insert`` are renamed to
``can_return_columns_from_insert`` and ``can_return_rows_from_bulk_insert``.
Miscellaneous Miscellaneous
------------- -------------

View File

@ -226,14 +226,14 @@ class BulkCreateTests(TestCase):
field_value = '' if isinstance(field, FileField) else None field_value = '' if isinstance(field, FileField) else None
self.assertEqual(NullableFields.objects.filter(**{field.name: field_value}).count(), 1) self.assertEqual(NullableFields.objects.filter(**{field.name: field_value}).count(), 1)
@skipUnlessDBFeature('can_return_ids_from_bulk_insert') @skipUnlessDBFeature('can_return_rows_from_bulk_insert')
def test_set_pk_and_insert_single_item(self): def test_set_pk_and_insert_single_item(self):
with self.assertNumQueries(1): with self.assertNumQueries(1):
countries = Country.objects.bulk_create([self.data[0]]) countries = Country.objects.bulk_create([self.data[0]])
self.assertEqual(len(countries), 1) self.assertEqual(len(countries), 1)
self.assertEqual(Country.objects.get(pk=countries[0].pk), countries[0]) self.assertEqual(Country.objects.get(pk=countries[0].pk), countries[0])
@skipUnlessDBFeature('can_return_ids_from_bulk_insert') @skipUnlessDBFeature('can_return_rows_from_bulk_insert')
def test_set_pk_and_query_efficiency(self): def test_set_pk_and_query_efficiency(self):
with self.assertNumQueries(1): with self.assertNumQueries(1):
countries = Country.objects.bulk_create(self.data) countries = Country.objects.bulk_create(self.data)
@ -243,7 +243,7 @@ class BulkCreateTests(TestCase):
self.assertEqual(Country.objects.get(pk=countries[2].pk), countries[2]) self.assertEqual(Country.objects.get(pk=countries[2].pk), countries[2])
self.assertEqual(Country.objects.get(pk=countries[3].pk), countries[3]) self.assertEqual(Country.objects.get(pk=countries[3].pk), countries[3])
@skipUnlessDBFeature('can_return_ids_from_bulk_insert') @skipUnlessDBFeature('can_return_rows_from_bulk_insert')
def test_set_state(self): def test_set_state(self):
country_nl = Country(name='Netherlands', iso_two_letter='NL') country_nl = Country(name='Netherlands', iso_two_letter='NL')
country_be = Country(name='Belgium', iso_two_letter='BE') country_be = Country(name='Belgium', iso_two_letter='BE')