diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-09-08 13:00:26 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-09-08 13:00:26 -0400 |
commit | 68a6701c6d72d6c6ef1ea2b7d615273659e8b735 (patch) | |
tree | aafd38ed52a00323f74af21b329d84571efcc1d1 | |
parent | 176ac6ab0915f99d378c7d9be67e9c0a73ab1800 (diff) | |
download | sqlalchemy-68a6701c6d72d6c6ef1ea2b7d615273659e8b735.tar.gz |
- Fixed bug in :meth:`.Session.bulk_save_objects` where a mapped
column that had some kind of "fetch on update" value and was not
locally present in the given object would cause an AttributeError
within the operation.
fixes #3525
-rw-r--r-- | doc/build/changelog/changelog_10.rst | 10 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/persistence.py | 8 | ||||
-rw-r--r-- | test/orm/test_bulk.py | 54 |
3 files changed, 70 insertions, 2 deletions
diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst index d12a51350..61dec24bf 100644 --- a/doc/build/changelog/changelog_10.rst +++ b/doc/build/changelog/changelog_10.rst @@ -19,6 +19,16 @@ :version: 1.0.9 .. change:: + :tags: bug, orm + :versions: 1.1.0b1 + :tickets: 3525 + + Fixed bug in :meth:`.Session.bulk_save_objects` where a mapped + column that had some kind of "fetch on update" value and was not + locally present in the given object would cause an AttributeError + within the operation. + + .. change:: :tags: bug, sql :versions: 1.1.0b1 :tickets: 3520 diff --git a/lib/sqlalchemy/orm/persistence.py b/lib/sqlalchemy/orm/persistence.py index c785a4dee..64872288e 100644 --- a/lib/sqlalchemy/orm/persistence.py +++ b/lib/sqlalchemy/orm/persistence.py @@ -672,6 +672,8 @@ def _emit_update_statements(base_mapper, uowtransaction, connection, value_params in records: c = cached_connections[connection].\ execute(statement, params) + + # TODO: why with bookkeeping=False? _postfetch( mapper, uowtransaction, @@ -694,6 +696,8 @@ def _emit_update_statements(base_mapper, uowtransaction, execute(statement, multiparams) rows += c.rowcount + + # TODO: why with bookkeeping=False? for state, state_dict, params, mapper, \ connection, value_params in records: _postfetch( @@ -964,6 +968,8 @@ def _postfetch(mapper, uowtransaction, table, after an INSERT or UPDATE statement has proceeded for that state.""" + # TODO: bulk is never non-False, need to clean this up + prefetch_cols = result.context.compiled.prefetch postfetch_cols = result.context.compiled.postfetch returning_cols = result.context.compiled.returning @@ -996,7 +1002,7 @@ def _postfetch(mapper, uowtransaction, table, mapper.class_manager.dispatch.refresh_flush( state, uowtransaction, load_evt_attrs) - if postfetch_cols: + if postfetch_cols and state: state._expire_attributes(state.dict, [mapper._columntoproperty[c].key for c in postfetch_cols if c in diff --git a/test/orm/test_bulk.py b/test/orm/test_bulk.py index e2a1464a6..7e1b0523f 100644 --- a/test/orm/test_bulk.py +++ b/test/orm/test_bulk.py @@ -2,7 +2,7 @@ from sqlalchemy import testing from sqlalchemy.testing import eq_ from sqlalchemy.testing.schema import Table, Column from sqlalchemy.testing import fixtures -from sqlalchemy import Integer, String, ForeignKey +from sqlalchemy import Integer, String, ForeignKey, FetchedValue from sqlalchemy.orm import mapper, Session from sqlalchemy.testing.assertsql import CompiledSQL from test.orm import _fixtures @@ -156,6 +156,58 @@ class BulkInsertUpdateTest(BulkTest, _fixtures.FixtureTest): ) +class BulkUDPostfetchTest(BulkTest, fixtures.MappedTest): + @classmethod + def define_tables(cls, metadata): + Table( + 'a', metadata, + Column( + 'id', Integer, + primary_key=True, + test_needs_autoincrement=True), + Column('x', Integer), + Column('y', Integer, server_default=FetchedValue(), server_onupdate=FetchedValue())) + + @classmethod + def setup_classes(cls): + class A(cls.Comparable): + pass + + @classmethod + def setup_mappers(cls): + A = cls.classes.A + a = cls.tables.a + + mapper(A, a) + + + def test_insert_w_fetch(self): + A = self.classes.A + + s = Session() + a1 = A(x=1) + s.bulk_save_objects([a1]) + s.commit() + + def test_update_w_fetch(self): + A = self.classes.A + + s = Session() + a1 = A(x=1, y=2) + s.add(a1) + s.commit() + + eq_(a1.id, 1) # force a load + a1.x = 5 + s.expire(a1, ['y']) + assert 'y' not in a1.__dict__ + s.bulk_save_objects([a1]) + s.commit() + + eq_(a1.x, 5) + eq_(a1.y, 2) + + class BulkInheritanceTest(BulkTest, fixtures.MappedTest): @classmethod def define_tables(cls, metadata): |