]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- add a new FAQ recipe for "walk all objects", replacing the need
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 10 Sep 2015 14:00:46 +0000 (10:00 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 10 Sep 2015 14:01:40 +0000 (10:01 -0400)
to use mapper.cascade_iterator() for this purpose as it was not really
designed for that use case.  Add docs to cascade_iterator() pointing
to the recipe.  fixes #3498

(cherry picked from commit 03797b78475bec9fb9c15f8e926414f3720a273c)

doc/build/faq/sessions.rst
lib/sqlalchemy/orm/mapper.py

index e3aae00ce2624ec5d01fedda6db1ff8e8fd1d410..a89b3765c7e94e22c1347effa0497875e4e48b51 100644 (file)
@@ -417,6 +417,77 @@ The recipe `ExpireRelationshipOnFKChange <http://www.sqlalchemy.org/trac/wiki/Us
 in order to coordinate the setting of foreign key attributes with many-to-one
 relationships.
 
+.. _faq_walk_objects:
+
+How do I walk all objects that are related to a given object?
+-------------------------------------------------------------
+
+An object that has other objects related to it will correspond to the
+:func:`.relationship` constructs set up between mappers.  This code fragment will
+iterate all the objects, correcting for cycles as well::
+
+    from sqlalchemy import inspect
+
+
+    def walk(obj):
+        stack = [obj]
+
+        seen = set()
+
+        while stack:
+            obj = stack.pop(0)
+            if obj in seen:
+                continue
+            else:
+                seen.add(obj)
+                yield obj
+            insp = inspect(obj)
+            for relationship in insp.mapper.relationships:
+                related = getattr(obj, relationship.key)
+                if relationship.uselist:
+                    stack.extend(related)
+                elif related is not None:
+                    stack.append(related)
+
+The function can be demonstrated as follows::
+
+    Base = declarative_base()
+
+
+    class A(Base):
+        __tablename__ = 'a'
+        id = Column(Integer, primary_key=True)
+        bs = relationship("B", backref="a")
+
+
+    class B(Base):
+        __tablename__ = 'b'
+        id = Column(Integer, primary_key=True)
+        a_id = Column(ForeignKey('a.id'))
+        c_id = Column(ForeignKey('c.id'))
+        c = relationship("C", backref="bs")
+
+
+    class C(Base):
+        __tablename__ = 'c'
+        id = Column(Integer, primary_key=True)
+
+
+    a1 = A(bs=[B(), B(c=C())])
+
+
+    for obj in walk(a1):
+        print obj
+
+Output::
+
+    <__main__.A object at 0x10303b190>
+    <__main__.B object at 0x103025210>
+    <__main__.B object at 0x10303b0d0>
+    <__main__.C object at 0x103025490>
+
+
+
 Is there a way to automagically have only unique keywords (or other kinds of objects) without doing a query for the keyword and getting a reference to the row containing that keyword?
 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
index 9690aa359e3d313d18c2f310834bdd7a82a334c5..5da694456e251bc5e855350ecbe312195208079e 100644 (file)
@@ -2557,15 +2557,24 @@ class Mapper(InspectionAttr):
         for all relationships that meet the given cascade rule.
 
         :param type_:
-          The name of the cascade rule (i.e. save-update, delete,
-          etc.)
+          The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``,
+          etc.).
+
+          .. note::  the ``"all"`` cascade is not accepted here.  For a generic
+             object traversal function, see :ref:`faq_walk_objects`.
 
         :param state:
           The lead InstanceState.  child items will be processed per
           the relationships defined for this object's mapper.
 
-        the return value are object instances; this provides a strong
-        reference so that they don't fall out of scope immediately.
+        :return: the method yields individual object instances.
+
+        .. seealso::
+
+            :ref:`unitofwork_cascades`
+
+            :ref:`faq_walk_objects` - illustrates a generic function to
+            traverse all objects without relying on cascades.
 
         """
         visited_states = set()