From: Mike Bayer Date: Wed, 14 Oct 2020 22:18:13 +0000 (-0400) Subject: move misplaced section X-Git-Tag: rel_1_4_0b1~27 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a2ea05800d142000208d4ce400ef1039316e1a5f;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git move misplaced section Change-Id: I472049fbdda70da94bbf1cf7b5e10d04721bc771 --- diff --git a/doc/build/orm/cascades.rst b/doc/build/orm/cascades.rst index 528e69a97b..217692c73c 100644 --- a/doc/build/orm/cascades.rst +++ b/doc/build/orm/cascades.rst @@ -603,3 +603,105 @@ course, :meth:`~.Session.add` ``i1`` to the session at a later point. This option may be helpful for situations where an object needs to be kept out of a session until it's construction is completed, but still needs to be given associations to objects which are already persistent in the target session. + + + +.. _session_deleting_from_collections: + +Notes on Delete - Deleting Objects Referenced from Collections and Scalar Relationships +---------------------------------------------------------------------------------------- + +The ORM in general never modifies the contents of a collection or scalar +relationship during the flush process. This means, if your class has a +:func:`_orm.relationship` that refers to a collection of objects, or a reference +to a single object such as many-to-one, the contents of this attribute will +not be modified when the flush process occurs. Instead, it is expected +that the :class:`.Session` would eventually be expired, either through the expire-on-commit behavior of +:meth:`.Session.commit` or through explicit use of :meth:`.Session.expire`. +At that point, any referenced object or collection associated with that +:class:`.Session` will be cleared and will re-load itself upon next access. + +A common confusion that arises regarding this behavior involves the use of the +:meth:`~.Session.delete` method. When :meth:`.Session.delete` is invoked upon +an object and the :class:`.Session` is flushed, the row is deleted from the +database. Rows that refer to the target row via foreign key, assuming they +are tracked using a :func:`_orm.relationship` between the two mapped object types, +will also see their foreign key attributes UPDATED to null, or if delete +cascade is set up, the related rows will be deleted as well. However, even +though rows related to the deleted object might be themselves modified as well, +**no changes occur to relationship-bound collections or object references on +the objects** involved in the operation within the scope of the flush +itself. This means if the object was a +member of a related collection, it will still be present on the Python side +until that collection is expired. Similarly, if the object were +referenced via many-to-one or one-to-one from another object, that reference +will remain present on that object until the object is expired as well. + +Below, we illustrate that after an ``Address`` object is marked +for deletion, it's still present in the collection associated with the +parent ``User``, even after a flush:: + + >>> address = user.addresses[1] + >>> session.delete(address) + >>> session.flush() + >>> address in user.addresses + True + +When the above session is committed, all attributes are expired. The next +access of ``user.addresses`` will re-load the collection, revealing the +desired state:: + + >>> session.commit() + >>> address in user.addresses + False + +There is a recipe for intercepting :meth:`.Session.delete` and invoking this +expiration automatically; see `ExpireRelationshipOnFKChange `_ for this. However, the usual practice of +deleting items within collections is to forego the usage of +:meth:`~.Session.delete` directly, and instead use cascade behavior to +automatically invoke the deletion as a result of removing the object from the +parent collection. The ``delete-orphan`` cascade accomplishes this, as +illustrated in the example below:: + + class User(Base): + __tablename__ = 'user' + + # ... + + addresses = relationship( + "Address", cascade="all, delete-orphan") + + # ... + + del user.addresses[1] + session.flush() + +Where above, upon removing the ``Address`` object from the ``User.addresses`` +collection, the ``delete-orphan`` cascade has the effect of marking the ``Address`` +object for deletion in the same way as passing it to :meth:`~.Session.delete`. + +The ``delete-orphan`` cascade can also be applied to a many-to-one +or one-to-one relationship, so that when an object is de-associated from its +parent, it is also automatically marked for deletion. Using ``delete-orphan`` +cascade on a many-to-one or one-to-one requires an additional flag +:paramref:`_orm.relationship.single_parent` which invokes an assertion +that this related object is not to shared with any other parent simultaneously:: + + class User(Base): + # ... + + preference = relationship( + "Preference", cascade="all, delete-orphan", + single_parent=True) + + +Above, if a hypothetical ``Preference`` object is removed from a ``User``, +it will be deleted on flush:: + + some_user.preference = None + session.flush() # will delete the Preference object + +.. seealso:: + + :ref:`unitofwork_cascades` for detail on cascades. + diff --git a/doc/build/orm/persistence_techniques.rst b/doc/build/orm/persistence_techniques.rst index 6e59924ccb..93686889f9 100644 --- a/doc/build/orm/persistence_techniques.rst +++ b/doc/build/orm/persistence_techniques.rst @@ -2,105 +2,6 @@ Additional Persistence Techniques ================================= -.. _session_deleting_from_collections: - -Notes on Delete - Deleting Objects Referenced from Collections and Scalar Relationships -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ORM in general never modifies the contents of a collection or scalar -relationship during the flush process. This means, if your class has a -:func:`_orm.relationship` that refers to a collection of objects, or a reference -to a single object such as many-to-one, the contents of this attribute will -not be modified when the flush process occurs. Instead, it is expected -that the :class:`.Session` would eventually be expired, either through the expire-on-commit behavior of -:meth:`.Session.commit` or through explicit use of :meth:`.Session.expire`. -At that point, any referenced object or collection associated with that -:class:`.Session` will be cleared and will re-load itself upon next access. - -A common confusion that arises regarding this behavior involves the use of the -:meth:`~.Session.delete` method. When :meth:`.Session.delete` is invoked upon -an object and the :class:`.Session` is flushed, the row is deleted from the -database. Rows that refer to the target row via foreign key, assuming they -are tracked using a :func:`_orm.relationship` between the two mapped object types, -will also see their foreign key attributes UPDATED to null, or if delete -cascade is set up, the related rows will be deleted as well. However, even -though rows related to the deleted object might be themselves modified as well, -**no changes occur to relationship-bound collections or object references on -the objects** involved in the operation within the scope of the flush -itself. This means if the object was a -member of a related collection, it will still be present on the Python side -until that collection is expired. Similarly, if the object were -referenced via many-to-one or one-to-one from another object, that reference -will remain present on that object until the object is expired as well. - -Below, we illustrate that after an ``Address`` object is marked -for deletion, it's still present in the collection associated with the -parent ``User``, even after a flush:: - - >>> address = user.addresses[1] - >>> session.delete(address) - >>> session.flush() - >>> address in user.addresses - True - -When the above session is committed, all attributes are expired. The next -access of ``user.addresses`` will re-load the collection, revealing the -desired state:: - - >>> session.commit() - >>> address in user.addresses - False - -There is a recipe for intercepting :meth:`.Session.delete` and invoking this -expiration automatically; see `ExpireRelationshipOnFKChange `_ for this. However, the usual practice of -deleting items within collections is to forego the usage of -:meth:`~.Session.delete` directly, and instead use cascade behavior to -automatically invoke the deletion as a result of removing the object from the -parent collection. The ``delete-orphan`` cascade accomplishes this, as -illustrated in the example below:: - - class User(Base): - __tablename__ = 'user' - - # ... - - addresses = relationship( - "Address", cascade="all, delete-orphan") - - # ... - - del user.addresses[1] - session.flush() - -Where above, upon removing the ``Address`` object from the ``User.addresses`` -collection, the ``delete-orphan`` cascade has the effect of marking the ``Address`` -object for deletion in the same way as passing it to :meth:`~.Session.delete`. - -The ``delete-orphan`` cascade can also be applied to a many-to-one -or one-to-one relationship, so that when an object is de-associated from its -parent, it is also automatically marked for deletion. Using ``delete-orphan`` -cascade on a many-to-one or one-to-one requires an additional flag -:paramref:`_orm.relationship.single_parent` which invokes an assertion -that this related object is not to shared with any other parent simultaneously:: - - class User(Base): - # ... - - preference = relationship( - "Preference", cascade="all, delete-orphan", - single_parent=True) - - -Above, if a hypothetical ``Preference`` object is removed from a ``User``, -it will be deleted on flush:: - - some_user.preference = None - session.flush() # will delete the Preference object - -.. seealso:: - - :ref:`unitofwork_cascades` for detail on cascades. - .. _flush_embedded_sql_expressions: