From: Mike Bayer Date: Fri, 8 May 2009 01:07:36 +0000 (+0000) Subject: - Fixed bug which prevented "mutable primary key" dependency X-Git-Tag: rel_0_5_4~10 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=d8c9dcc0ad108d7f85cfa41181e5a959783fdd65;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Fixed bug which prevented "mutable primary key" dependency logic from functioning properly on a one-to-one relation(). [ticket:1406] - moved MySQL to use innodb for naturalpks tests --- diff --git a/CHANGES b/CHANGES index 5d415a01b3..a6bedd4bf6 100644 --- a/CHANGES +++ b/CHANGES @@ -44,7 +44,11 @@ CHANGES - Fixed another location where autoflush was interfering with session.merge(). autoflush is disabled completely for the duration of merge() now. [ticket:1360] - + + - Fixed bug which prevented "mutable primary key" dependency + logic from functioning properly on a one-to-one + relation(). [ticket:1406] + - Fixed bug in relation(), introduced in 0.5.3, whereby a self referential relation from a base class to a joined-table subclass would diff --git a/lib/sqlalchemy/orm/dependency.py b/lib/sqlalchemy/orm/dependency.py index c4ba7852f9..a80727b7f2 100644 --- a/lib/sqlalchemy/orm/dependency.py +++ b/lib/sqlalchemy/orm/dependency.py @@ -261,7 +261,8 @@ class OneToManyDP(DependencyProcessor): if not history: history = uowcommit.get_attribute_history(state, self.key, passive=False) for child in history.unchanged: - uowcommit.register_object(child) + if child is not None: + uowcommit.register_object(child) def _synchronize(self, state, child, associationrow, clearkeys, uowcommit): source = state diff --git a/test/orm/naturalpks.py b/test/orm/naturalpks.py index 980165fc0b..c546953a37 100644 --- a/test/orm/naturalpks.py +++ b/test/orm/naturalpks.py @@ -14,20 +14,23 @@ class NaturalPKTest(_base.MappedTest): def define_tables(self, metadata): users = Table('users', metadata, Column('username', String(50), primary_key=True), - Column('fullname', String(100))) + Column('fullname', String(100)), + test_needs_fk=True) addresses = Table('addresses', metadata, Column('email', String(50), primary_key=True), - Column('username', String(50), ForeignKey('users.username', onupdate="cascade"))) + Column('username', String(50), ForeignKey('users.username', onupdate="cascade")), + test_needs_fk=True) items = Table('items', metadata, Column('itemname', String(50), primary_key=True), - Column('description', String(100))) + Column('description', String(100)), + test_needs_fk=True) users_to_items = Table('users_to_items', metadata, Column('username', String(50), ForeignKey('users.username', onupdate='cascade'), primary_key=True), Column('itemname', String(50), ForeignKey('items.itemname', onupdate='cascade'), primary_key=True), - ) + test_needs_fk=True) def setup_classes(self): class User(_base.ComparableEntity): @@ -101,8 +104,7 @@ class NaturalPKTest(_base.MappedTest): assert sess.query(User).get('ed').fullname == 'jack' - @testing.fails_on('mysql', 'FIXME: unknown') - @testing.fails_on('sqlite', 'FIXME: unknown') + @testing.fails_on('sqlite', 'sqlite doesnt support ON UPDATE CASCADE') def test_onetomany_passive(self): self._test_onetomany(True) @@ -153,8 +155,7 @@ class NaturalPKTest(_base.MappedTest): self.assertEquals(User(username='fred', fullname='jack'), u1) - @testing.fails_on('sqlite', 'FIXME: unknown') - @testing.fails_on('mysql', 'FIXME: unknown') + @testing.fails_on('sqlite', 'sqlite doesnt support ON UPDATE CASCADE') def test_manytoone_passive(self): self._test_manytoone(True) @@ -181,8 +182,6 @@ class NaturalPKTest(_base.MappedTest): u1.username = 'ed' - print id(a1), id(a2), id(u1) - print sa.orm.attributes.instance_state(u1).parents def go(): sess.flush() if passive_updates: @@ -198,8 +197,48 @@ class NaturalPKTest(_base.MappedTest): sess.expunge_all() self.assertEquals([Address(username='ed'), Address(username='ed')], sess.query(Address).all()) - @testing.fails_on('sqlite', 'FIXME: unknown') - @testing.fails_on('mysql', 'FIXME: unknown') + @testing.fails_on('sqlite', 'sqlite doesnt support ON UPDATE CASCADE') + def test_onetoone_passive(self): + self._test_onetoone(True) + + def test_onetoone_nonpassive(self): + self._test_onetoone(False) + + @testing.resolve_artifact_names + def _test_onetoone(self, passive_updates): + mapper(User, users, properties={ + "address":relation(Address, passive_updates=passive_updates, uselist=False) + }) + mapper(Address, addresses) + + sess = create_session() + u1 = User(username='jack', fullname='jack') + sess.add(u1) + sess.flush() + + a1 = Address(email='jack1') + u1.address = a1 + sess.add(a1) + sess.flush() + + u1.username = 'ed' + + def go(): + sess.flush() + if passive_updates: + self.assert_sql_count(testing.db, go, 1) + else: + self.assert_sql_count(testing.db, go, 2) + + def go(): + sess.flush() + self.assert_sql_count(testing.db, go, 0) + + assert a1.username == 'ed' + sess.expunge_all() + self.assertEquals([Address(username='ed')], sess.query(Address).all()) + + @testing.fails_on('sqlite', 'sqlite doesnt support ON UPDATE CASCADE') def test_bidirectional_passive(self): self._test_bidirectional(True) @@ -252,11 +291,11 @@ class NaturalPKTest(_base.MappedTest): self.assertEquals([Address(username='fred'), Address(username='fred')], sess.query(Address).all()) - @testing.fails_on('sqlite', 'FIXME: unknown') - @testing.fails_on('mysql', 'FIXME: unknown') + @testing.fails_on('sqlite', 'sqlite doesnt support ON UPDATE CASCADE') def test_manytomany_passive(self): self._test_manytomany(True) + @testing.fails_on('mysql', 'the executemany() of the association table fails to report the correct row count') def test_manytomany_nonpassive(self): self._test_manytomany(False) @@ -369,8 +408,7 @@ class NonPKCascadeTest(_base.MappedTest): class Address(_base.ComparableEntity): pass - @testing.fails_on('sqlite', 'FIXME: unknown') - @testing.fails_on('mysql', 'FIXME: unknown') + @testing.fails_on('sqlite', 'sqlite doesnt support ON UPDATE CASCADE') def test_onetomany_passive(self): self._test_onetomany(True) diff --git a/test/orm/onetoone.py b/test/orm/onetoone.py index 1ed3dcc619..be0375e48b 100644 --- a/test/orm/onetoone.py +++ b/test/orm/onetoone.py @@ -1,7 +1,7 @@ import testenv; testenv.configure_for_tests() from testlib import sa, testing from testlib.sa import Table, Column, Integer, String, ForeignKey -from testlib.sa.orm import mapper, relation +from testlib.sa.orm import mapper, relation, create_session from orm import _base @@ -19,50 +19,56 @@ class O2OTest(_base.MappedTest): Column('description', String(100)), Column('jack_id', Integer, ForeignKey("jack.id"))) + @testing.resolve_artifact_names def setup_mappers(self): class Jack(_base.BasicEntity): pass class Port(_base.BasicEntity): pass - @testing.resolve_artifact_names - def test_1(self): - ctx = sa.orm.scoped_session(sa.orm.create_session) - mapper(Port, port, extension=ctx.extension) + @testing.resolve_artifact_names + def test_basic(self): + mapper(Port, port) mapper(Jack, jack, order_by=[jack.c.number], properties=dict( port=relation(Port, backref='jack', - uselist=False, lazy=True)), - extension=ctx.extension) + uselist=False, + )), + ) + + session = create_session() j = Jack(number='101') + session.add(j) p = Port(name='fa0/1') + session.add(p) + j.port=p - ctx.flush() + session.flush() jid = j.id pid = p.id - j=ctx.query(Jack).get(jid) - p=ctx.query(Port).get(pid) + j=session.query(Jack).get(jid) + p=session.query(Port).get(pid) assert p.jack is not None assert p.jack is j assert j.port is not None p.jack = None assert j.port is None - ctx.expunge_all() + session.expunge_all() - j = ctx.query(Jack).get(jid) - p = ctx.query(Port).get(pid) + j = session.query(Jack).get(jid) + p = session.query(Port).get(pid) j.port=None self.assert_(p.jack is None) - ctx.flush() + session.flush() - ctx.delete(j) - ctx.flush() + session.delete(j) + session.flush() if __name__ == "__main__": testenv.main()