summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2013-01-24 21:31:23 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2013-01-24 21:31:23 -0500
commita477f8a61ec60b2fc343d87aa30ef6595c77727d (patch)
treea3768faf5fb9513cefebaa02f122bd1565f057ce /lib/sqlalchemy
parentb1df6fab53a0d740fe60f04e5c9ad01027ba59af (diff)
downloadsqlalchemy-a477f8a61ec60b2fc343d87aa30ef6595c77727d.tar.gz
the consideration of a pending object as
an "orphan" has been modified to more closely match the behavior as that of persistent objects, which is that the object is expunged from the :class:`.Session` as soon as it is de-associated from any of its orphan-enabled parents. Previously, the pending object would be expunged only if de-associated from all of its orphan-enabled parents. The new flag ``legacy_is_orphan`` is added to :func:`.orm.mapper` which re-establishes the legacy behavior. [ticket:2655]
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/__init__.py26
-rw-r--r--lib/sqlalchemy/orm/mapper.py25
-rw-r--r--lib/sqlalchemy/orm/properties.py2
3 files changed, 45 insertions, 8 deletions
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py
index 0b035ef9b..e9dde3ca7 100644
--- a/lib/sqlalchemy/orm/__init__.py
+++ b/lib/sqlalchemy/orm/__init__.py
@@ -947,6 +947,32 @@ def mapper(class_, local_table=None, *args, **params):
this parameter can be used to specify which columns are "foreign".
In most cases can be left as ``None``.
+ :param legacy_is_orphan: Boolean, defaults to ``False``.
+ When ``True``, specifies that "legacy" orphan consideration
+ is to be applied to objects mapped by this mapper, which means
+ that a pending (that is, not persistent) object is auto-expunged
+ from an owning :class:`.Session` only when it is de-associated
+ from *all* parents that specify a ``delete-orphan`` cascade towards
+ this mapper. The new default behavior is that the object is auto-expunged
+ when it is de-associated with *any* of its parents that specify
+ ``delete-orphan`` cascade. This behavior is more consistent with
+ that of a persistent object, and allows behavior to be consistent
+ in more scenarios independently of whether or not an orphanable
+ object has been flushed yet or not.
+
+ See the change note and example at :ref:`legacy_is_orphan_addition`
+ for more detail on this change.
+
+ .. versionadded:: 0.8 - the consideration of a pending object as
+ an "orphan" has been modified to more closely match the
+ behavior as that of persistent objects, which is that the object
+ is expunged from the :class:`.Session` as soon as it is
+ de-associated from any of its orphan-enabled parents. Previously,
+ the pending object would be expunged only if de-associated
+ from all of its orphan-enabled parents. The new flag ``legacy_is_orphan``
+ is added to :func:`.orm.mapper` which re-establishes the
+ legacy behavior.
+
:param non_primary: Specify that this :class:`.Mapper` is in addition
to the "primary" mapper, that is, the one used for persistence.
The :class:`.Mapper` created here may be used for ad-hoc
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index eb43cc53e..447a5fce1 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -113,6 +113,7 @@ class Mapper(_InspectionAttr):
exclude_properties=None,
passive_updates=True,
eager_defaults=False,
+ legacy_is_orphan=False,
_compiled_cache_size=100,
):
"""Construct a new mapper.
@@ -145,7 +146,7 @@ class Mapper(_InspectionAttr):
self.inherit_condition = inherit_condition
self.inherit_foreign_keys = inherit_foreign_keys
self._init_properties = properties or {}
- self.delete_orphans = []
+ self._delete_orphans = []
self.batch = batch
self.eager_defaults = eager_defaults
self.column_prefix = column_prefix
@@ -154,6 +155,7 @@ class Mapper(_InspectionAttr):
self._dependency_processors = []
self.validators = util.immutabledict()
self.passive_updates = passive_updates
+ self.legacy_is_orphan = legacy_is_orphan
self._clause_adapter = None
self._requires_row_aliasing = False
self._inherits_equated_pairs = None
@@ -1292,14 +1294,23 @@ class Mapper(_InspectionAttr):
)
def _is_orphan(self, state):
- o = False
+ orphan_possible = False
for mapper in self.iterate_to_root():
- for (key, cls) in mapper.delete_orphans:
- if attributes.manager_of_class(cls).has_parent(
- state, key, optimistic=bool(state.key)):
+ for (key, cls) in mapper._delete_orphans:
+ orphan_possible = True
+
+ has_parent = attributes.manager_of_class(cls).has_parent(
+ state, key, optimistic=state.has_identity)
+
+ if self.legacy_is_orphan and has_parent:
return False
- o = o or bool(mapper.delete_orphans)
- return o
+ elif not self.legacy_is_orphan and not has_parent:
+ return True
+
+ if self.legacy_is_orphan:
+ return orphan_possible
+ else:
+ return False
def has_property(self, key):
return key in self._props
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index bc238d973..c618e89b2 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -1081,7 +1081,7 @@ class RelationshipProperty(StrategizedProperty):
self.target = self.mapper.mapped_table
if self.cascade.delete_orphan:
- self.mapper.primary_mapper().delete_orphans.append(
+ self.mapper.primary_mapper()._delete_orphans.append(
(self.key, self.parent.class_)
)