]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add doc note for multiple table mapping
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 23 Oct 2019 15:18:56 +0000 (11:18 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 23 Oct 2019 15:20:27 +0000 (11:20 -0400)
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)

doc/build/orm/nonstandard_mappings.rst

index 9cb87221f5f9a5422e7120272223d8b157ee7f2d..7516d1b54dd32bad81e227e28cb26f80e404de5e 100644 (file)
@@ -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
 =========================================