mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-31 12:58:45 -04:00
beginning to address cycles but its not worked out yet
This commit is contained in:
@@ -191,10 +191,73 @@ class OneToManyDP(DependencyProcessor):
|
||||
uow.dependencies.update([
|
||||
(parent_saves, after_save),
|
||||
(after_save, child_saves),
|
||||
(child_deletes, before_delete),
|
||||
(before_delete, parent_deletes)
|
||||
|
||||
(child_saves, parent_deletes),
|
||||
(before_delete, child_saves),
|
||||
|
||||
(child_deletes, parent_deletes)
|
||||
])
|
||||
|
||||
def per_saved_state_flush_actions(self, uow, state):
|
||||
if True:
|
||||
parent_saves = unitofwork.SaveUpdateAll(uow, self.parent)
|
||||
child_saves = unitofwork.SaveUpdateAll(uow, self.mapper)
|
||||
assert parent_saves in uow.cycles
|
||||
assert child_saves in uow.cycles
|
||||
|
||||
added, updated, deleted = uow.get_attribute_history(state, self.key, passive=True)
|
||||
if not added and not unchanged and not deleted:
|
||||
return
|
||||
|
||||
save_parent = unitofwork.SaveUpdateState(state)
|
||||
after_save = unitofwork.ProcessState(uow, self, False, state)
|
||||
|
||||
for child_state in added + unchanged + deleted:
|
||||
if child_state is None:
|
||||
continue
|
||||
|
||||
(deleted, listonly) = uow.states[child_state]
|
||||
if deleted:
|
||||
child_action = unitofwork.DeleteState(child_state)
|
||||
else:
|
||||
child_action = unitofwork.SaveUpdateState(child_state)
|
||||
|
||||
uow.dependencies.update([
|
||||
(save_parent, after_save),
|
||||
(after_save, child_action),
|
||||
])
|
||||
|
||||
def per_deleted_state_flush_actions(self, uow, state):
|
||||
if True:
|
||||
parent_deletes = unitofwork.DeleteAll(uow, self.parent)
|
||||
child_deletes = unitofwork.DeleteAll(uow, self.mapper)
|
||||
assert parent_deletes in uow.cycles
|
||||
assert child_deletes in uow.cycles
|
||||
|
||||
added, updated, deleted = uow.get_attribute_history(state, self.key, passive=True)
|
||||
if not added and not unchanged and not deleted:
|
||||
return
|
||||
|
||||
delete_parent = unitofwork.DeleteState(state)
|
||||
after_delete = unitofwork.ProcessState(uow, self, True, state)
|
||||
|
||||
for child_state in added + unchanged + deleted:
|
||||
if child_state is None:
|
||||
continue
|
||||
|
||||
(deleted, listonly) = uow.states[child_state]
|
||||
if deleted:
|
||||
child_action = unitofwork.DeleteState(child_state)
|
||||
else:
|
||||
child_action = unitofwork.SaveUpdateState(child_state)
|
||||
|
||||
uow.dependencies.update([
|
||||
(child_action, )
|
||||
(save_parent, after_save),
|
||||
(after_save, child_action),
|
||||
])
|
||||
|
||||
|
||||
def presort_deletes(self, uowcommit, states):
|
||||
# head object is being deleted, and we manage its list of child objects
|
||||
# the child objects have to have their foreign key to the parent set to NULL
|
||||
@@ -318,12 +381,11 @@ class ManyToOneDP(DependencyProcessor):
|
||||
else:
|
||||
unitofwork.GetDependentObjects(uow, self, False, True)
|
||||
unitofwork.GetDependentObjects(uow, self, True, True)
|
||||
|
||||
|
||||
uow.dependencies.update([
|
||||
(after_save, parent_saves),
|
||||
(child_saves, after_save),
|
||||
(parent_deletes, before_delete),
|
||||
(before_delete, child_deletes)
|
||||
(after_save, parent_saves),
|
||||
(parent_saves, child_deletes)
|
||||
])
|
||||
|
||||
def presort_deletes(self, uowcommit, states):
|
||||
@@ -375,6 +437,7 @@ class ManyToOneDP(DependencyProcessor):
|
||||
if history:
|
||||
for child in history.added:
|
||||
self._synchronize(state, child, None, False, uowcommit)
|
||||
|
||||
self._conditional_post_update(state, uowcommit, history.sum())
|
||||
|
||||
|
||||
|
||||
@@ -166,16 +166,23 @@ class UOWTransaction(object):
|
||||
if not ret:
|
||||
break
|
||||
|
||||
self.cycles = cycles = topological.find_cycles(self.dependencies, self.postsort_actions.values())
|
||||
assert not cycles
|
||||
for rec in cycles:
|
||||
rec.per_state_flush_actions(self)
|
||||
|
||||
for edge in list(self.dependencies):
|
||||
# both nodes in this edge were part of a cycle.
|
||||
# remove that from our deps as it has replaced
|
||||
# itself with per-state actions
|
||||
if cycles.issuperset(edge):
|
||||
self.dependencies.remove(edge)
|
||||
|
||||
sort = topological.sort(self.dependencies, self.postsort_actions.values())
|
||||
print sort
|
||||
for rec in sort:
|
||||
rec.execute(self)
|
||||
|
||||
# if cycles:
|
||||
# break up actions into finer grained actions along those cycles
|
||||
|
||||
# for rec in topological.sort(self.dependencies, self.actions):
|
||||
# rec.execute()
|
||||
|
||||
def finalize_flush_changes(self):
|
||||
"""mark processed objects as clean / deleted after a successful flush().
|
||||
@@ -276,6 +283,13 @@ class ProcessAll(PropertyRecMixin, PostSortRec):
|
||||
else:
|
||||
self.dependency_processor.process_saves(uow, states)
|
||||
|
||||
def per_state_flush_actions(self, uow):
|
||||
for state in self._elements(uow):
|
||||
if self.delete:
|
||||
self.dependency_processor.per_deleted_state_flush_actions(uow, self.dependency_processor, state)
|
||||
else:
|
||||
self.dependency_processor.per_saved_state_flush_actions(uow, self.dependency_processor, state)
|
||||
|
||||
class SaveUpdateAll(PostSortRec):
|
||||
def __init__(self, uow, mapper):
|
||||
self.mapper = mapper
|
||||
@@ -285,6 +299,10 @@ class SaveUpdateAll(PostSortRec):
|
||||
uow.states_for_mapper_hierarchy(self.mapper, False, False),
|
||||
uow
|
||||
)
|
||||
|
||||
def per_state_flush_actions(self, uow):
|
||||
for state in uow.states_for_mapper_hierarchy(self.mapper, False, False):
|
||||
SaveUpdateState(uow, state)
|
||||
|
||||
class DeleteAll(PostSortRec):
|
||||
def __init__(self, uow, mapper):
|
||||
@@ -296,6 +314,10 @@ class DeleteAll(PostSortRec):
|
||||
uow
|
||||
)
|
||||
|
||||
def per_state_flush_actions(self, uow):
|
||||
for state in uow.states_for_mapper_hierarchy(self.mapper, True, False):
|
||||
DeleteState(uow, state)
|
||||
|
||||
class ProcessState(PostSortRec):
|
||||
def __init__(self, uow, dependency_processor, delete, state):
|
||||
self.dependency_processor = dependency_processor
|
||||
|
||||
@@ -15,7 +15,7 @@ class UOWTest(_fixtures.FixtureTest, testing.AssertsExecutionResults):
|
||||
|
||||
class RudimentaryFlushTest(UOWTest):
|
||||
|
||||
def test_one_to_many(self):
|
||||
def test_one_to_many_save(self):
|
||||
mapper(User, users, properties={
|
||||
'addresses':relationship(Address),
|
||||
})
|
||||
@@ -44,6 +44,17 @@ class RudimentaryFlushTest(UOWTest):
|
||||
lambda ctx: {'email_address': 'a2', 'user_id':u1.id}
|
||||
),
|
||||
)
|
||||
|
||||
def test_one_to_many_delete_all(self):
|
||||
mapper(User, users, properties={
|
||||
'addresses':relationship(Address),
|
||||
})
|
||||
mapper(Address, addresses)
|
||||
sess = create_session()
|
||||
a1, a2 = Address(email_address='a1'), Address(email_address='a2')
|
||||
u1 = User(name='u1', addresses=[a1, a2])
|
||||
sess.add(u1)
|
||||
sess.flush()
|
||||
|
||||
sess.delete(u1)
|
||||
sess.delete(a1)
|
||||
@@ -60,8 +71,37 @@ class RudimentaryFlushTest(UOWTest):
|
||||
{'id':u1.id}
|
||||
),
|
||||
)
|
||||
|
||||
def test_one_to_many_delete_parent(self):
|
||||
mapper(User, users, properties={
|
||||
'addresses':relationship(Address),
|
||||
})
|
||||
mapper(Address, addresses)
|
||||
sess = create_session()
|
||||
a1, a2 = Address(email_address='a1'), Address(email_address='a2')
|
||||
u1 = User(name='u1', addresses=[a1, a2])
|
||||
sess.add(u1)
|
||||
sess.flush()
|
||||
|
||||
sess.delete(u1)
|
||||
self.assert_sql_execution(
|
||||
testing.db,
|
||||
sess.flush,
|
||||
CompiledSQL(
|
||||
"UPDATE addresses SET user_id=:user_id WHERE addresses.id = :addresses_id",
|
||||
[{u'addresses_id': 1, 'user_id': None}]
|
||||
),
|
||||
CompiledSQL(
|
||||
"UPDATE addresses SET user_id=:user_id WHERE addresses.id = :addresses_id",
|
||||
[{u'addresses_id': 2, 'user_id': None}]
|
||||
),
|
||||
CompiledSQL(
|
||||
"DELETE FROM users WHERE users.id = :id",
|
||||
{'id':u1.id}
|
||||
),
|
||||
)
|
||||
|
||||
def test_many_to_one(self):
|
||||
def test_many_to_one_save(self):
|
||||
|
||||
mapper(User, users)
|
||||
mapper(Address, addresses, properties={
|
||||
@@ -93,6 +133,19 @@ class RudimentaryFlushTest(UOWTest):
|
||||
),
|
||||
)
|
||||
|
||||
def test_many_to_one_delete_all(self):
|
||||
mapper(User, users)
|
||||
mapper(Address, addresses, properties={
|
||||
'user':relationship(User)
|
||||
})
|
||||
sess = create_session()
|
||||
|
||||
u1 = User(name='u1')
|
||||
a1, a2 = Address(email_address='a1', user=u1), \
|
||||
Address(email_address='a2', user=u1)
|
||||
sess.add_all([a1, a2])
|
||||
sess.flush()
|
||||
|
||||
sess.delete(u1)
|
||||
sess.delete(a1)
|
||||
sess.delete(a2)
|
||||
@@ -108,4 +161,84 @@ class RudimentaryFlushTest(UOWTest):
|
||||
{'id':u1.id}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def test_many_to_one_delete_target(self):
|
||||
mapper(User, users)
|
||||
mapper(Address, addresses, properties={
|
||||
'user':relationship(User)
|
||||
})
|
||||
sess = create_session()
|
||||
|
||||
u1 = User(name='u1')
|
||||
a1, a2 = Address(email_address='a1', user=u1), \
|
||||
Address(email_address='a2', user=u1)
|
||||
sess.add_all([a1, a2])
|
||||
sess.flush()
|
||||
|
||||
sess.delete(u1)
|
||||
a1.user = a2.user = None
|
||||
self.assert_sql_execution(
|
||||
testing.db,
|
||||
sess.flush,
|
||||
CompiledSQL(
|
||||
"UPDATE addresses SET user_id=:user_id WHERE addresses.id = :addresses_id",
|
||||
[{u'addresses_id': 1, 'user_id': None}]
|
||||
),
|
||||
CompiledSQL(
|
||||
"UPDATE addresses SET user_id=:user_id WHERE addresses.id = :addresses_id",
|
||||
[{u'addresses_id': 2, 'user_id': None}]
|
||||
),
|
||||
CompiledSQL(
|
||||
"DELETE FROM users WHERE users.id = :id",
|
||||
{'id':u1.id}
|
||||
),
|
||||
)
|
||||
|
||||
class SingleCycleTest(UOWTest):
|
||||
def test_one_to_many(self):
|
||||
mapper(Node, nodes, properties={
|
||||
'children':relationship(Node)
|
||||
})
|
||||
sess = create_session()
|
||||
|
||||
n2, n3 = Node(data='n2'), Node(data='n3')
|
||||
n1 = Node(data='n1', children=[n2, n3])
|
||||
|
||||
sess.add(n1)
|
||||
|
||||
self.assert_sql_execution(
|
||||
testing.db,
|
||||
sess.flush,
|
||||
CompiledSQL(
|
||||
"INSERT INTO nodes (data) VALUES (:data)",
|
||||
{'data': 'n1'}
|
||||
),
|
||||
CompiledSQL(
|
||||
"INSERT INTO addresses (user_id, email_address) "
|
||||
"VALUES (:user_id, :email_address)",
|
||||
lambda ctx: {'email_address': 'a1', 'user_id':u1.id}
|
||||
),
|
||||
CompiledSQL(
|
||||
"INSERT INTO addresses (user_id, email_address) "
|
||||
"VALUES (:user_id, :email_address)",
|
||||
lambda ctx: {'email_address': 'a2', 'user_id':u1.id}
|
||||
),
|
||||
)
|
||||
|
||||
return
|
||||
sess.delete(n1)
|
||||
sess.delete(n2)
|
||||
sess.delete(n3)
|
||||
self.assert_sql_execution(
|
||||
testing.db,
|
||||
sess.flush,
|
||||
CompiledSQL(
|
||||
"DELETE FROM addresses WHERE addresses.id = :id",
|
||||
[{'id':a1.id},{'id':a2.id}]
|
||||
),
|
||||
CompiledSQL(
|
||||
"DELETE FROM users WHERE users.id = :id",
|
||||
{'id':u1.id}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user