def test_one_to_many_delete_parent(self): mapper(Node, nodes, properties={'children': relationship(Node)}) sess = create_session() n2, n3 = Node(data='n2', children=[]), Node(data='n3', children=[]) n1 = Node(data='n1', children=[n2, n3]) sess.add(n1) sess.flush() sess.delete(n1) self.assert_sql_execution( testing.db, sess.flush, AllOf( CompiledSQL( "UPDATE nodes SET parent_id=:parent_id " "WHERE nodes.id = :nodes_id", lambda ctx: { 'nodes_id': n3.id, 'parent_id': None }), CompiledSQL( "UPDATE nodes SET parent_id=:parent_id " "WHERE nodes.id = :nodes_id", lambda ctx: { 'nodes_id': n2.id, 'parent_id': None }), ), CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id", lambda ctx: {'id': n1.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) sess.delete(a2) 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}), )
def test_one_to_many_save(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) self.assert_sql_execution( testing.db, sess.flush, CompiledSQL("INSERT INTO users (name) VALUES (:name)", {'name': 'u1'}), 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 }), )
def test_many_to_one_delete_unloaded(self): mapper(User, users) mapper(Address, addresses, properties={'parent': relationship(User)}) parent = User(name='p1') c1, c2 = Address(email_address='c1', parent=parent), \ Address(email_address='c2', parent=parent) session = Session() session.add_all([c1, c2]) session.add(parent) session.flush() pid = parent.id c1id = c1.id c2id = c2.id session.expire(parent) session.expire(c1) session.expire(c2) session.delete(c1) session.delete(c2) session.delete(parent) # testing that relationships # are loaded even if all ids/references are # expired self.assert_sql_execution( testing.db, session.flush, AllOf( # ensure all three m2os are loaded. # the selects here are in fact unexpiring # each row - the m2o comes from the identity map. CompiledSQL( "SELECT addresses.id AS addresses_id, addresses.user_id AS " "addresses_user_id, addresses.email_address AS " "addresses_email_address FROM addresses WHERE addresses.id = " ":param_1", lambda ctx: {'param_1': c1id}), CompiledSQL( "SELECT addresses.id AS addresses_id, addresses.user_id AS " "addresses_user_id, addresses.email_address AS " "addresses_email_address FROM addresses WHERE addresses.id = " ":param_1", lambda ctx: {'param_1': c2id}), CompiledSQL( "SELECT users.id AS users_id, users.name AS users_name " "FROM users WHERE users.id = :param_1", lambda ctx: {'param_1': pid}), ), CompiledSQL("DELETE FROM addresses WHERE addresses.id = :id", lambda ctx: [{ 'id': c1id }, { 'id': c2id }]), CompiledSQL("DELETE FROM users WHERE users.id = :id", lambda ctx: {'id': pid}), )
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", lambda ctx: [{ u'addresses_id': a1.id, 'user_id': None }]), CompiledSQL( "UPDATE addresses SET user_id=:user_id WHERE " "addresses.id = :addresses_id", lambda ctx: [{ u'addresses_id': a2.id, 'user_id': None }]), CompiledSQL("DELETE FROM users WHERE users.id = :id", {'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", lambda ctx: [{ u'addresses_id': a1.id, 'user_id': None }]), CompiledSQL( "UPDATE addresses SET user_id=:user_id WHERE " "addresses.id = :addresses_id", lambda ctx: [{ u'addresses_id': a2.id, 'user_id': None }]), CompiledSQL("DELETE FROM users WHERE users.id = :id", {'id': u1.id}), )
def test_many_to_one_delete_all(self): mapper( Node, nodes, properties={'parent': relationship(Node, remote_side=nodes.c.id)}) sess = create_session() n1 = Node(data='n1') n2, n3 = Node(data='n2', parent=n1), Node(data='n3', parent=n1) sess.add_all([n2, n3]) sess.flush() sess.delete(n1) sess.delete(n2) sess.delete(n3) self.assert_sql_execution( testing.db, sess.flush, CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id", lambda ctx: [{ 'id': n2.id }, { 'id': n3.id }]), CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id", lambda ctx: {'id': n1.id}))
def test_many_to_one_save(self): mapper( Node, nodes, properties={'parent': relationship(Node, remote_side=nodes.c.id)}) sess = create_session() n1 = Node(data='n1') n2, n3 = Node(data='n2', parent=n1), Node(data='n3', parent=n1) sess.add_all([n2, n3]) self.assert_sql_execution( testing.db, sess.flush, CompiledSQL( "INSERT INTO nodes (parent_id, data) VALUES " "(:parent_id, :data)", { 'parent_id': None, 'data': 'n1' }), AllOf( CompiledSQL( "INSERT INTO nodes (parent_id, data) VALUES " "(:parent_id, :data)", lambda ctx: { 'parent_id': n1.id, 'data': 'n2' }), CompiledSQL( "INSERT INTO nodes (parent_id, data) VALUES " "(:parent_id, :data)", lambda ctx: { 'parent_id': n1.id, 'data': 'n3' }), ))
def test_one(self): mapper(Parent, parent, properties={ 'c1s':relationship(Child1, primaryjoin=child1.c.parent_id==parent.c.id), 'c2s':relationship(Child2, primaryjoin=child2.c.parent_id==parent.c.id), 'c3s':relationship(Child3, primaryjoin=child3.c.parent_id==parent.c.id), 'c1':relationship(Child1, primaryjoin=child1.c.id==parent.c.c1_id, post_update=True), 'c2':relationship(Child2, primaryjoin=child2.c.id==parent.c.c2_id, post_update=True), 'c3':relationship(Child3, primaryjoin=child3.c.id==parent.c.c3_id, post_update=True), }) mapper(Child1, child1) mapper(Child2, child2) mapper(Child3, child3) sess = create_session() p1 = Parent('p1') c11, c12, c13 = Child1('c1'), Child1('c2'), Child1('c3') c21, c22, c23 = Child2('c1'), Child2('c2'), Child2('c3') c31, c32, c33 = Child3('c1'), Child3('c2'), Child3('c3') p1.c1s = [c11, c12, c13] p1.c2s = [c21, c22, c23] p1.c3s = [c31, c32, c33] sess.add(p1) sess.flush() p1.c1 = c12 p1.c2 = c23 p1.c3 = c31 self.assert_sql_execution( testing.db, sess.flush, CompiledSQL( "UPDATE parent SET c1_id=:c1_id, c2_id=:c2_id, " "c3_id=:c3_id WHERE parent.id = :parent_id", lambda ctx: {'c2_id': c23.id, 'parent_id': p1.id, 'c1_id': c12.id, 'c3_id': c31.id} ) ) p1.c1 = p1.c2 = p1.c3 = None self.assert_sql_execution( testing.db, sess.flush, CompiledSQL( "UPDATE parent SET c1_id=:c1_id, c2_id=:c2_id, " "c3_id=:c3_id WHERE parent.id = :parent_id", lambda ctx: {'c2_id': None, 'parent_id': p1.id, 'c1_id': None, 'c3_id': None} ) )
def test_rowswitch_doesntfire(self): mapper(User, users) mapper(Address, addresses, properties={ 'user':relationship(User, passive_updates=True) }) sess = create_session() u1 = User(username='******') a1 = Address(user=u1, email='ed@host1') sess.add(u1) sess.add(a1) sess.flush() sess.delete(u1) sess.delete(a1) u2 = User(username='******') a2 = Address(user=u2, email='ed@host1', etc='foo') sess.add(u2) sess.add(a2) from sqlalchemy.test.assertsql import CompiledSQL # test that the primary key columns of addresses are not # being updated as well, since this is a row switch. self.assert_sql_execution(testing.db, sess.flush, CompiledSQL( "UPDATE addresses SET etc=:etc WHERE " "addresses.username = :addresses_username AND" " addresses.email = :addresses_email", {'etc': 'foo', 'addresses_username':'******', 'addresses_email':'ed@host1'} ), )
def test_many_to_many(self): mapper(Item, items, properties={ 'keywords':relationship(Keyword, secondary=item_keywords) }) mapper(Keyword, keywords) sess = create_session() k1 = Keyword(name='k1') i1 = Item(description='i1', keywords=[k1]) sess.add(i1) self.assert_sql_execution( testing.db, sess.flush, AllOf( CompiledSQL( "INSERT INTO keywords (name) VALUES (:name)", {'name':'k1'} ), CompiledSQL( "INSERT INTO items (description) VALUES (:description)", {'description':'i1'} ), ), CompiledSQL( "INSERT INTO item_keywords (item_id, keyword_id) " "VALUES (:item_id, :keyword_id)", lambda ctx:{'item_id':i1.id, 'keyword_id':k1.id} ) ) # test that keywords collection isn't loaded sess.expire(i1, ['keywords']) i1.description = 'i2' self.assert_sql_execution( testing.db, sess.flush, CompiledSQL("UPDATE items SET description=:description " "WHERE items.id = :items_id", lambda ctx:{'description':'i2', 'items_id':i1.id}) )
def test_one_to_many_delete_all(self): mapper(Node, nodes, properties={'children': relationship(Node)}) sess = create_session() n2, n3 = Node(data='n2', children=[]), Node(data='n3', children=[]) n1 = Node(data='n1', children=[n2, n3]) sess.add(n1) sess.flush() sess.delete(n1) sess.delete(n2) sess.delete(n3) self.assert_sql_execution( testing.db, sess.flush, CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id", lambda ctx: [{ 'id': n2.id }, { 'id': n3.id }]), CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id", lambda ctx: {'id': n1.id}))
def testbasic(self): """Post_update only fires off when needed. This test case used to produce many superfluous update statements, particularly upon delete """ mapper(Node, node, properties={ 'children': relation( Node, primaryjoin=node.c.id==node.c.parent_id, lazy=True, cascade="all", backref=backref("parent", remote_side=node.c.id) ), 'prev_sibling': relation( Node, primaryjoin=node.c.prev_sibling_id==node.c.id, remote_side=node.c.id, lazy=True, uselist=False), 'next_sibling': relation( Node, primaryjoin=node.c.next_sibling_id==node.c.id, remote_side=node.c.id, lazy=True, uselist=False, post_update=True)}) session = create_session() def append_child(parent, child): if parent.children: parent.children[-1].next_sibling = child child.prev_sibling = parent.children[-1] parent.children.append(child) def remove_child(parent, child): child.parent = None node = child.next_sibling node.prev_sibling = child.prev_sibling child.prev_sibling.next_sibling = node session.delete(child) root = Node('root') about = Node('about') cats = Node('cats') stories = Node('stories') bruce = Node('bruce') append_child(root, about) assert(about.prev_sibling is None) append_child(root, cats) assert(cats.prev_sibling is about) assert(cats.next_sibling is None) assert(about.next_sibling is cats) assert(about.prev_sibling is None) append_child(root, stories) append_child(root, bruce) session.add(root) session.flush() remove_child(root, cats) # pre-trigger lazy loader on 'cats' to make the test easier cats.children self.assert_sql_execution( testing.db, session.flush, CompiledSQL("UPDATE node SET prev_sibling_id=:prev_sibling_id " "WHERE node.id = :node_id", lambda ctx:{'prev_sibling_id':about.id, 'node_id':stories.id}), CompiledSQL("UPDATE node SET next_sibling_id=:next_sibling_id " "WHERE node.id = :node_id", lambda ctx:{'next_sibling_id':stories.id, 'node_id':about.id}), CompiledSQL("UPDATE node SET next_sibling_id=:next_sibling_id " "WHERE node.id = :node_id", lambda ctx:{'next_sibling_id':None, 'node_id':cats.id}), CompiledSQL("DELETE FROM node WHERE node.id = :id", lambda ctx:[{'id':cats.id}]) )
def testpostupdate_o2m(self): """A cycle between two rows, with a post_update on the one-to-many""" mapper(Ball, ball) mapper(Person, person, properties=dict( balls=relation(Ball, primaryjoin=ball.c.person_id == person.c.id, remote_side=ball.c.person_id, cascade="all, delete-orphan", post_update=True, backref='person'), favorite=relation(Ball, primaryjoin=person.c.favorite_ball_id == ball.c.id, remote_side=person.c.favorite_ball_id))) b = Ball(data='some data') p = Person(data='some data') p.balls.append(b) b2 = Ball(data='some data') p.balls.append(b2) b3 = Ball(data='some data') p.balls.append(b3) b4 = Ball(data='some data') p.balls.append(b4) p.favorite = b sess = create_session() sess.add_all((b,p,b2,b3,b4)) self.assert_sql_execution( testing.db, sess.flush, CompiledSQL("INSERT INTO ball (person_id, data) " "VALUES (:person_id, :data)", {'person_id':None, 'data':'some data'}), CompiledSQL("INSERT INTO ball (person_id, data) " "VALUES (:person_id, :data)", {'person_id':None, 'data':'some data'}), CompiledSQL("INSERT INTO ball (person_id, data) " "VALUES (:person_id, :data)", {'person_id':None, 'data':'some data'}), CompiledSQL("INSERT INTO ball (person_id, data) " "VALUES (:person_id, :data)", {'person_id':None, 'data':'some data'}), CompiledSQL("INSERT INTO person (favorite_ball_id, data) " "VALUES (:favorite_ball_id, :data)", lambda ctx:{'favorite_ball_id':b.id, 'data':'some data'}), AllOf( CompiledSQL("UPDATE ball SET person_id=:person_id " "WHERE ball.id = :ball_id", lambda ctx:{'person_id':p.id,'ball_id':b.id}), CompiledSQL("UPDATE ball SET person_id=:person_id " "WHERE ball.id = :ball_id", lambda ctx:{'person_id':p.id,'ball_id':b2.id}), CompiledSQL("UPDATE ball SET person_id=:person_id " "WHERE ball.id = :ball_id", lambda ctx:{'person_id':p.id,'ball_id':b3.id}), CompiledSQL("UPDATE ball SET person_id=:person_id " "WHERE ball.id = :ball_id", lambda ctx:{'person_id':p.id,'ball_id':b4.id}) ), ) sess.delete(p) self.assert_sql_execution(testing.db, sess.flush, AllOf(CompiledSQL("UPDATE ball SET person_id=:person_id " "WHERE ball.id = :ball_id", lambda ctx:{'person_id': None, 'ball_id': b.id}), CompiledSQL("UPDATE ball SET person_id=:person_id " "WHERE ball.id = :ball_id", lambda ctx:{'person_id': None, 'ball_id': b2.id}), CompiledSQL("UPDATE ball SET person_id=:person_id " "WHERE ball.id = :ball_id", lambda ctx:{'person_id': None, 'ball_id': b3.id}), CompiledSQL("UPDATE ball SET person_id=:person_id " "WHERE ball.id = :ball_id", lambda ctx:{'person_id': None, 'ball_id': b4.id})), CompiledSQL("DELETE FROM person WHERE person.id = :id", lambda ctx:[{'id':p.id}]), CompiledSQL("DELETE FROM ball WHERE ball.id = :id", lambda ctx:[{'id': b.id}, {'id': b2.id}, {'id': b3.id}, {'id': b4.id}]) )
def test_many_to_many_one(self): class Node(Base): pass mapper( Node, nodes, properties={ 'children': relationship( Node, secondary=node_to_nodes, primaryjoin=nodes.c.id == node_to_nodes.c.left_node_id, secondaryjoin=nodes.c.id == node_to_nodes.c.right_node_id, backref='parents'), 'favorite': relationship(Node, remote_side=nodes.c.id) }) sess = create_session() n1 = Node(data='n1') n2 = Node(data='n2') n3 = Node(data='n3') n4 = Node(data='n4') n5 = Node(data='n5') n4.favorite = n3 n1.favorite = n5 n5.favorite = n2 n1.children = [n2, n3, n4] n2.children = [n3, n5] n3.children = [n5, n4] sess.add_all([n1, n2, n3, n4, n5]) # can't really assert the SQL on this easily # since there's too many ways to insert the rows. # so check the end result sess.flush() eq_( sess.query(node_to_nodes.c.left_node_id, node_to_nodes.c.right_node_id).\ order_by(node_to_nodes.c.left_node_id, node_to_nodes.c.right_node_id).\ all(), sorted([ (n1.id, n2.id), (n1.id, n3.id), (n1.id, n4.id), (n2.id, n3.id), (n2.id, n5.id), (n3.id, n5.id), (n3.id, n4.id) ]) ) sess.delete(n1) self.assert_sql_execution( testing.db, sess.flush, # this is n1.parents firing off, as it should, since # passive_deletes is False for n1.parents CompiledSQL( "SELECT nodes.id AS nodes_id, nodes.data AS nodes_data, " "nodes.favorite_node_id AS nodes_favorite_node_id FROM " "nodes, node_to_nodes WHERE :param_1 = " "node_to_nodes.right_node_id AND nodes.id = " "node_to_nodes.left_node_id", lambda ctx: {u'param_1': n1.id}, ), CompiledSQL( "DELETE FROM node_to_nodes WHERE " "node_to_nodes.left_node_id = :left_node_id AND " "node_to_nodes.right_node_id = :right_node_id", lambda ctx: [{ 'right_node_id': n2.id, 'left_node_id': n1.id }, { 'right_node_id': n3.id, 'left_node_id': n1.id }, { 'right_node_id': n4.id, 'left_node_id': n1.id }]), CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id", lambda ctx: {'id': n1.id}), ) for n in [n2, n3, n4, n5]: sess.delete(n) # load these collections # outside of the flush() below n4.children n5.children self.assert_sql_execution( testing.db, sess.flush, CompiledSQL( "DELETE FROM node_to_nodes WHERE node_to_nodes.left_node_id " "= :left_node_id AND node_to_nodes.right_node_id = " ":right_node_id", lambda ctx: [{ 'right_node_id': n5.id, 'left_node_id': n3.id }, { 'right_node_id': n4.id, 'left_node_id': n3.id }, { 'right_node_id': n3.id, 'left_node_id': n2.id }, { 'right_node_id': n5.id, 'left_node_id': n2.id }]), CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id", lambda ctx: [{ 'id': n4.id }, { 'id': n5.id }]), CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id", lambda ctx: [{ 'id': n2.id }, { 'id': n3.id }]), )
def test_delete_unloaded_m2o(self): mapper( Node, nodes, properties={'parent': relationship(Node, remote_side=nodes.c.id)}) parent = Node() c1, c2 = Node(parent=parent), Node(parent=parent) session = Session() session.add_all([c1, c2]) session.add(parent) session.flush() pid = parent.id c1id = c1.id c2id = c2.id session.expire(parent) session.expire(c1) session.expire(c2) session.delete(c1) session.delete(c2) session.delete(parent) # testing that relationships # are loaded even if all ids/references are # expired self.assert_sql_execution( testing.db, session.flush, AllOf( # ensure all three m2os are loaded. # the selects here are in fact unexpiring # each row - the m2o comes from the identity map. CompiledSQL( "SELECT nodes.id AS nodes_id, nodes.parent_id AS " "nodes_parent_id, " "nodes.data AS nodes_data FROM nodes " "WHERE nodes.id = :param_1", lambda ctx: {'param_1': pid}), CompiledSQL( "SELECT nodes.id AS nodes_id, nodes.parent_id AS " "nodes_parent_id, " "nodes.data AS nodes_data FROM nodes " "WHERE nodes.id = :param_1", lambda ctx: {'param_1': c1id}), CompiledSQL( "SELECT nodes.id AS nodes_id, nodes.parent_id AS " "nodes_parent_id, " "nodes.data AS nodes_data FROM nodes " "WHERE nodes.id = :param_1", lambda ctx: {'param_1': c2id}), ), CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id", lambda ctx: [{ 'id': c1id }, { 'id': c2id }]), CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id", lambda ctx: {'id': pid}), )
def test_bidirectional_multilevel_save(self): mapper(Node, nodes, properties={ 'children': relationship(Node, backref=backref('parent', remote_side=nodes.c.id)) }) sess = create_session() n1 = Node(data='n1') n1.children.append(Node(data='n11')) n12 = Node(data='n12') n1.children.append(n12) n1.children.append(Node(data='n13')) n1.children[1].children.append(Node(data='n121')) n1.children[1].children.append(Node(data='n122')) n1.children[1].children.append(Node(data='n123')) sess.add(n1) self.assert_sql_execution( testing.db, sess.flush, CompiledSQL( "INSERT INTO nodes (parent_id, data) VALUES " "(:parent_id, :data)", lambda ctx: { 'parent_id': None, 'data': 'n1' }), CompiledSQL( "INSERT INTO nodes (parent_id, data) VALUES " "(:parent_id, :data)", lambda ctx: { 'parent_id': n1.id, 'data': 'n11' }), CompiledSQL( "INSERT INTO nodes (parent_id, data) VALUES " "(:parent_id, :data)", lambda ctx: { 'parent_id': n1.id, 'data': 'n12' }), CompiledSQL( "INSERT INTO nodes (parent_id, data) VALUES " "(:parent_id, :data)", lambda ctx: { 'parent_id': n1.id, 'data': 'n13' }), CompiledSQL( "INSERT INTO nodes (parent_id, data) VALUES " "(:parent_id, :data)", lambda ctx: { 'parent_id': n12.id, 'data': 'n121' }), CompiledSQL( "INSERT INTO nodes (parent_id, data) VALUES " "(:parent_id, :data)", lambda ctx: { 'parent_id': n12.id, 'data': 'n122' }), CompiledSQL( "INSERT INTO nodes (parent_id, data) VALUES " "(:parent_id, :data)", lambda ctx: { 'parent_id': n12.id, 'data': 'n123' }), )