From: Mike Bayer Date: Wed, 23 Oct 2019 15:18:56 +0000 (-0400) Subject: Add doc note for multiple table mapping X-Git-Tag: rel_1_3_11~27 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cdf6761e879baa2af03e13b5a7a3f10ae4d9ac91;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Add doc note for multiple table mapping When mapping to a construct like OUTER JOIN, an UPDATE from the ORM expects that all involved tables have a row already present; document this as well as a potential workaround. Fixes: #4927 Change-Id: Ie99f9f53cea33d9df2513f384a7c68676b197fb7 (cherry picked from commit 2cae55269b6745bd733be142a6e199d8ecf9b50a) --- diff --git a/doc/build/orm/nonstandard_mappings.rst b/doc/build/orm/nonstandard_mappings.rst index 9cb87221f5..7516d1b54d 100644 --- a/doc/build/orm/nonstandard_mappings.rst +++ b/doc/build/orm/nonstandard_mappings.rst @@ -69,6 +69,39 @@ is represented from an ``AddressUser`` object as ``(AddressUser.id, AddressUser.address_id)``. +.. note:: + + A mapping against multiple tables as illustrated above supports + persistence, that is, INSERT, UPDATE and DELETE of rows within the targeted + tables. However, it does not support an operation that would UPDATE one + table and perform INSERT or DELETE on others at the same time for one + record. That is, if a record PtoQ is mapped to tables “p” and “q”, where it + has a row based on a LEFT OUTER JOIN of “p” and “q”, if an UPDATE proceeds + that is to alter data in the “q” table in an existing record, the row in + “q” must exist; it won’t emit an INSERT if the primary key identity is + already present. If the row does not exist, for most DBAPI drivers which + support reporting the number of rows affected by an UPDATE, the ORM will + fail to detect an updated row and raise an error; otherwise, the data + would be silently ignored. + + A recipe to allow for an on-the-fly “insert” of the related row might make + use of the .MapperEvents.before_update event and look like:: + + from sqlalchemy import event + + @event.listens_for(PtoQ, 'before_update') + def receive_before_update(mapper, connection, target): + if target.some_required_attr_on_q is None: + connection.execute(q_table.insert(), {"id": target.id}) + + where above, a row is INSERTed into the ``q_table`` table by creating an + INSERT construct with :meth:`.Table.insert`, then executing it using the + given :class:`.Connection` which is the same one being used to emit other + SQL for the flush process. The user-supplied logic would have to detect + that the LEFT OUTER JOIN from "p" to "q" does not have an entry for the "q" + side. + + Mapping a Class against Arbitrary Selects =========================================