diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-09-10 10:00:46 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-09-10 10:00:46 -0400 |
commit | 03797b78475bec9fb9c15f8e926414f3720a273c (patch) | |
tree | e5aa7d9ce00e4184a48f46ccf27e974fa4ae1453 | |
parent | db824b535359abef145303556a227ccda3cdcccc (diff) | |
download | sqlalchemy-03797b78475bec9fb9c15f8e926414f3720a273c.tar.gz |
- add a new FAQ recipe for "walk all objects", replacing the need
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
-rw-r--r-- | doc/build/faq/sessions.rst | 71 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/mapper.py | 17 |
2 files changed, 84 insertions, 4 deletions
diff --git a/doc/build/faq/sessions.rst b/doc/build/faq/sessions.rst index e3aae00ce..a89b3765c 100644 --- a/doc/build/faq/sessions.rst +++ b/doc/build/faq/sessions.rst @@ -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? --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 1a46667c5..21577f5ea 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -2571,15 +2571,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() |