summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/__init__.py4
-rw-r--r--lib/sqlalchemy/orm/dynamic.py45
2 files changed, 31 insertions, 18 deletions
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py
index 7f5672371..7ce298f71 100644
--- a/lib/sqlalchemy/orm/__init__.py
+++ b/lib/sqlalchemy/orm/__init__.py
@@ -245,7 +245,8 @@ def relation(argument, secondary=None, **kwargs):
return PropertyLoader(argument, secondary=secondary, **kwargs)
def dynamic_loader(argument, secondary=None, primaryjoin=None, secondaryjoin=None, entity_name=None,
- foreign_keys=None, backref=None, post_update=False, cascade=None, remote_side=None, enable_typechecks=True):
+ foreign_keys=None, backref=None, post_update=False, cascade=None, remote_side=None, enable_typechecks=True,
+ passive_deletes=False):
"""construct a dynamically-loading mapper property.
This property is similar to relation(), except read operations
@@ -263,6 +264,7 @@ def dynamic_loader(argument, secondary=None, primaryjoin=None, secondaryjoin=Non
return PropertyLoader(argument, secondary=secondary, primaryjoin=primaryjoin,
secondaryjoin=secondaryjoin, entity_name=entity_name, foreign_keys=foreign_keys, backref=backref,
post_update=post_update, cascade=cascade, remote_side=remote_side, enable_typechecks=enable_typechecks,
+ passive_deletes=passive_deletes,
strategy_class=DynaLoader)
#def _relation_loader(mapper, secondary=None, primaryjoin=None, secondaryjoin=None, lazy=True, **kwargs):
diff --git a/lib/sqlalchemy/orm/dynamic.py b/lib/sqlalchemy/orm/dynamic.py
index 0c49bcfc3..f5662a933 100644
--- a/lib/sqlalchemy/orm/dynamic.py
+++ b/lib/sqlalchemy/orm/dynamic.py
@@ -1,8 +1,8 @@
"""'dynamic' collection API. returns Query() objects on the 'read' side, alters
a special AttributeHistory on the 'write' side."""
-from sqlalchemy import exceptions
-from sqlalchemy.orm import attributes, object_session
+from sqlalchemy import exceptions, util
+from sqlalchemy.orm import attributes, object_session, util as mapperutil
from sqlalchemy.orm.query import Query
from sqlalchemy.orm.mapper import has_identity, object_mapper
@@ -23,7 +23,7 @@ class DynamicAttributeImpl(attributes.AttributeImpl):
state.dict[self.key] = CollectionHistory(self, state)
def get_collection(self, state, user_data=None):
- return self.get_history(state)._added_items
+ return self.get_history(state, passive=True)._added_items
def set(self, state, value, initiator):
if initiator is self:
@@ -39,19 +39,23 @@ class DynamicAttributeImpl(attributes.AttributeImpl):
def get_history(self, state, passive=False):
try:
- return state.dict[self.key]
+ c = state.dict[self.key]
except KeyError:
state.dict[self.key] = c = CollectionHistory(self, state)
+
+ if not passive:
+ return CollectionHistory(self, state, apply_to=c)
+ else:
return c
def append(self, state, value, initiator, passive=False):
if initiator is not self:
- self.get_history(state)._added_items.append(value)
+ self.get_history(state, passive=True)._added_items.append(value)
self.fire_append_event(state, value, initiator)
def remove(self, state, value, initiator, passive=False):
if initiator is not self:
- self.get_history(state)._deleted_items.append(value)
+ self.get_history(state, passive=True)._deleted_items.append(value)
self.fire_remove_event(state, value, initiator)
@@ -64,7 +68,7 @@ class AppenderQuery(Query):
def __session(self):
instance = self.state.obj()
sess = object_session(instance)
- if sess is not None and instance in sess and sess.autoflush:
+ if sess is not None and self.autoflush and sess.autoflush and instance in sess:
sess.flush()
if not has_identity(instance):
return None
@@ -74,14 +78,14 @@ class AppenderQuery(Query):
def __iter__(self):
sess = self.__session()
if sess is None:
- return iter(self.attr.get_history(self.state)._added_items)
+ return iter(self.attr.get_history(self.state, passive=True)._added_items)
else:
return iter(self._clone(sess))
def __getitem__(self, index):
sess = self.__session()
if sess is None:
- return self.attr.get_history(self.state)._added_items.__getitem__(index)
+ return self.attr.get_history(self.state, passive=True)._added_items.__getitem__(index)
else:
return self._clone(sess).__getitem__(index)
@@ -96,7 +100,7 @@ class AppenderQuery(Query):
try:
sess = object_mapper(instance).get_session()
except exceptions.InvalidRequestError:
- raise exceptions.InvalidRequestError("Parent instance %s is not bound to a Session, and no contextual session is established; lazy load operation of attribute '%s' cannot proceed" % (self.instance.__class__, self.key))
+ raise exceptions.InvalidRequestError("Parent instance %s is not bound to a Session, and no contextual session is established; lazy load operation of attribute '%s' cannot proceed" % (mapperutil.instance_str(instance), self.attr.key))
return sess.query(self.attr.target_mapper).with_parent(instance)
@@ -106,7 +110,7 @@ class AppenderQuery(Query):
oldlist = list(self)
else:
oldlist = []
- self.attr.get_history(self.state).replace(oldlist, collection)
+ self.attr.get_history(self.state, passive=True).replace(oldlist, collection)
return oldlist
def append(self, item):
@@ -119,12 +123,19 @@ class AppenderQuery(Query):
class CollectionHistory(attributes.AttributeHistory):
"""Overrides AttributeHistory to receive append/remove events directly."""
- def __init__(self, attr, state):
- self._deleted_items = []
- self._added_items = []
- self._unchanged_items = []
- self._state = state
-
+ def __init__(self, attr, state, apply_to=None):
+ if apply_to:
+ deleted = util.IdentitySet(apply_to._deleted_items)
+ added = apply_to._added_items
+ coll = AppenderQuery(attr, state).autoflush(False)
+ self._unchanged_items = [o for o in util.IdentitySet(coll) if o not in deleted]
+ self._added_items = apply_to._added_items
+ self._deleted_items = apply_to._deleted_items
+ else:
+ self._deleted_items = []
+ self._added_items = []
+ self._unchanged_items = []
+
def replace(self, olditems, newitems):
self._added_items = newitems
self._deleted_items = olditems