--- /dev/null
+.. change::
+ :tags: feature, sql
+ :tickets: 3314
+
+ The tuple returned by :attr:`.CursorResult.inserted_primary_key` is now a
+ :class:`_result.Row` object with a named tuple interface on top of the
+ existing tuple interface.
+
+
+
to be populated regardless of whether or not "autoincrement" were used, hence
to express a complete primary key it's a tuple.
+.. versionchanged:: 1.4.8 the tuple returned by
+ :attr:`_engine.CursorResult.inserted_primary_key` is now a named tuple
+ fullfilled by returning it as a :class:`_result.Row` object.
+
.. _tutorial_core_insert_values_clause:
INSERT usually generates the "values" clause automatically
def inserted_primary_key(self):
"""Return the primary key for the row just inserted.
- The return value is a list of scalar values
- corresponding to the list of primary key columns
- in the target table.
+ The return value is a :class:`_result.Row` object representing
+ a named tuple of primary key values in the order in which the
+ primary key columns are configured in the source
+ :class:`_schema.Table`.
- This only applies to single row :func:`_expression.insert`
+ .. versionchanged:: 1.4.8 - the
+ :attr:`_engine.CursorResult.inserted_primary_key`
+ value is now a named tuple via the :class:`_result.Row` class,
+ rather than a plain tuple.
+
+ This accessor only applies to single row :func:`_expression.insert`
constructs which did not explicitly specify
- :meth:`_expression.Insert.returning`.
-
- Note that primary key columns which specify a
- server_default clause,
- or otherwise do not qualify as "autoincrement"
- columns (see the notes at :class:`_schema.Column`), and were
- generated using the database-side default, will
- appear in this list as ``None`` unless the backend
- supports "returning" and the insert statement executed
- with the "implicit returning" enabled.
+ :meth:`_expression.Insert.returning`. Support for multirow inserts,
+ while not yet available for most backends, would be accessed using
+ the :attr:`_engine.CursorResult.inserted_primary_key_rows` accessor.
+
+ Note that primary key columns which specify a server_default clause, or
+ otherwise do not qualify as "autoincrement" columns (see the notes at
+ :class:`_schema.Column`), and were generated using the database-side
+ default, will appear in this list as ``None`` unless the backend
+ supports "returning" and the insert statement executed with the
+ "implicit returning" enabled.
Raises :class:`~sqlalchemy.exc.InvalidRequestError` if the executed
statement is not a compiled expression construct
)
@util.memoized_property
+ @util.preload_module("sqlalchemy.engine.result")
def _inserted_primary_key_from_lastrowid_getter(self):
+ result = util.preloaded.engine_result
+
key_getter = self._key_getters_for_crud_column[2]
table = self.statement.table
else:
proc = None
+ row_fn = result.result_tuple([col.key for col in table.primary_key])
+
def get(lastrowid, parameters):
if proc is not None:
lastrowid = proc(lastrowid)
if lastrowid is None:
- return tuple(getter(parameters) for getter, col in getters)
+ return row_fn(getter(parameters) for getter, col in getters)
else:
- return tuple(
+ return row_fn(
lastrowid if col is autoinc_col else getter(parameters)
for getter, col in getters
)
return get
@util.memoized_property
+ @util.preload_module("sqlalchemy.engine.result")
def _inserted_primary_key_from_returning_getter(self):
+ result = util.preloaded.engine_result
+
key_getter = self._key_getters_for_crud_column[2]
table = self.statement.table
for col in table.primary_key
]
+ row_fn = result.result_tuple([col.key for col in table.primary_key])
+
def get(row, parameters):
- return tuple(
+ return row_fn(
getter(row) if use_row else getter(parameters)
for getter, use_row in getters
)
result = connection.execute(table_.insert(), values)
ret = values.copy()
- for col, id_ in zip(
- table_.primary_key, result.inserted_primary_key
- ):
+ ipk = result.inserted_primary_key
+ for col, id_ in zip(table_.primary_key, ipk):
ret[col.key] = id_
if result.lastrow_has_defaults():
).first()
for c in table_.c:
ret[c.key] = row._mapping[c]
- return ret
+ return ret, ipk
if testing.against("firebird", "postgresql", "oracle", "mssql"):
assert testing.db.dialect.implicit_returning
for engine in test_engines:
try:
table_.create(bind=engine, checkfirst=True)
- i = insert_values(engine, table_, values)
+ i, ipk = insert_values(engine, table_, values)
eq_(i, assertvalues)
+
+ # named tuple tests
+ for col in table_.primary_key:
+ eq_(getattr(ipk, col.key), assertvalues[col.key])
+ eq_(ipk._mapping[col.key], assertvalues[col.key])
+
+ eq_(
+ ipk._fields, tuple([col.key for col in table_.primary_key])
+ )
+
finally:
table_.drop(bind=engine)