diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/sqlalchemy/orm/__init__.py | 14 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/properties.py | 5 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/session.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/unitofwork.py | 7 |
4 files changed, 25 insertions, 3 deletions
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py index 39c68f0aa..8b32d1a27 100644 --- a/lib/sqlalchemy/orm/__init__.py +++ b/lib/sqlalchemy/orm/__init__.py @@ -255,7 +255,19 @@ def relationship(argument, secondary=None, **kwargs): * ``all`` - shorthand for "save-update,merge, refresh-expire, expunge, delete" - + + :param cascade_backrefs=True: + a boolean value indicating if the ``save-update`` cascade should + operate along a backref event. When set to ``False`` on a + one-to-many relationship that has a many-to-one backref, assigning + a persistent object to the many-to-one attribute on a transient object + will not add the transient to the session. Similarly, when + set to ``False`` on a many-to-one relationship that has a one-to-many + backref, appending a persistent object to the one-to-many collection + on a transient object will not add the transient to the session. + + ``cascade_backrefs`` is new in 0.6.5. + :param collection_class: a class or callable that returns a new list-holding object. will be used in place of a plain list for storing elements. diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 80443a7f3..a5e6930b2 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -444,8 +444,10 @@ class RelationshipProperty(StrategizedProperty): comparator_factory=None, single_parent=False, innerjoin=False, doc=None, + cascade_backrefs=True, load_on_pending=False, - strategy_class=None, _local_remote_pairs=None, query_class=None): + strategy_class=None, _local_remote_pairs=None, + query_class=None): self.uselist = uselist self.argument = argument @@ -460,6 +462,7 @@ class RelationshipProperty(StrategizedProperty): self._user_defined_foreign_keys = foreign_keys self.collection_class = collection_class self.passive_deletes = passive_deletes + self.cascade_backrefs = cascade_backrefs self.passive_updates = passive_updates self.remote_side = remote_side self.enable_typechecks = enable_typechecks diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index bab98f4fa..d97ef87e7 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -1153,6 +1153,8 @@ class Session(object): This operation cascades to associated instances if the association is mapped with ``cascade="merge"``. + See :ref:`unitofwork_merging` for a detailed discussion of merging. + """ if 'dont_load' in kw: load = not kw['dont_load'] diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index 830ac3c0c..a9808e6ba 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -33,10 +33,13 @@ class UOWEventHandler(interfaces.AttributeExtension): def append(self, state, item, initiator): # process "save_update" cascade rules for when # an instance is appended to the list of another instance + sess = _state_session(state) if sess: prop = _state_mapper(state).get_property(self.key) - if prop.cascade.save_update and item not in sess: + if prop.cascade.save_update and \ + (prop.cascade_backrefs or self.key == initiator.key) and \ + item not in sess: sess.add(item) return item @@ -55,11 +58,13 @@ class UOWEventHandler(interfaces.AttributeExtension): # is attached to another instance if oldvalue is newvalue: return newvalue + sess = _state_session(state) if sess: prop = _state_mapper(state).get_property(self.key) if newvalue is not None and \ prop.cascade.save_update and \ + (prop.cascade_backrefs or self.key == initiator.key) and \ newvalue not in sess: sess.add(newvalue) if prop.cascade.delete_orphan and \ |
