summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/sqlalchemy/orm/__init__.py14
-rw-r--r--lib/sqlalchemy/orm/properties.py5
-rw-r--r--lib/sqlalchemy/orm/session.py2
-rw-r--r--lib/sqlalchemy/orm/unitofwork.py7
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 \