From a52ffc36476916988742098888ffae43a9b48e74 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Fri, 4 Dec 2009 01:51:23 +0000 Subject: [PATCH] - an explicit check occurs when a synonym() is used with map_column=True, when a ColumnProperty (deferred or otherwise) exists separately in the properties dictionary sent to mapper with the same keyname. Instead of silently replacing the existing property (and possible options on that property), an error is raised. [ticket:1633] --- CHANGES | 7 +++++++ lib/sqlalchemy/orm/mapper.py | 22 ++++++++++++++++++++-- lib/sqlalchemy/orm/strategies.py | 2 +- test/orm/test_mapper.py | 20 ++++++++++++++++++++ 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index a8d4a0109e..052848fbf9 100644 --- a/CHANGES +++ b/CHANGES @@ -69,6 +69,13 @@ CHANGES in filter criterion against the dynamic relation. [ticket:1531] + - an explicit check occurs when a synonym() is used with + map_column=True, when a ColumnProperty (deferred or otherwise) + exists separately in the properties dictionary sent to mapper + with the same keyname. Instead of silently replacing + the existing property (and possible options on that property), + an error is raised. [ticket:1633] + - a "dynamic" loader sets up its query criterion at construction time so that the actual query is returned from non-cloning accessors like "statement". diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 1c7c37ead7..9b5583afaf 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -616,8 +616,26 @@ class Mapper(object): raise sa_exc.ArgumentError( "Can't compile synonym '%s': no column on table '%s' named '%s'" % (prop.name, self.mapped_table.description, key)) - self._configure_property(prop.name, ColumnProperty(self.mapped_table.c[key]), init=init, setparent=setparent) - + elif self.mapped_table.c[key] in self._columntoproperty and \ + self._columntoproperty[self.mapped_table.c[key]].key == prop.name: + raise sa_exc.ArgumentError( + "Can't call map_column=True for synonym %r=%r, " + "a ColumnProperty already exists keyed to the name %r " + "for column %r" % + (key, prop.name, prop.name, key) + ) + p = ColumnProperty(self.mapped_table.c[key]) + self._configure_property(prop.name, p, init=init, setparent=setparent) + p._mapped_by_synonym = key + + if key in self._props and getattr(self._props[key], '_mapped_by_synonym', False): + syn = self._props[key]._mapped_by_synonym + raise sa_exc.ArgumentError( + "Can't call map_column=True for synonym %r=%r, " + "a ColumnProperty already exists keyed to the name " + "%r for column %r" % (syn, key, key, syn) + ) + self._props[key] = prop prop.key = key diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index ff80fd6e6b..7c56e1182a 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -195,7 +195,7 @@ log.class_logger(CompositeColumnLoader) class DeferredColumnLoader(LoaderStrategy): """Strategize the loading of a deferred column-based MapperProperty.""" - + def create_row_processor(self, selectcontext, path, mapper, row, adapter): col = self.columns[0] if adapter: diff --git a/test/orm/test_mapper.py b/test/orm/test_mapper.py index 62bf636b89..4c174ba24d 100644 --- a/test/orm/test_mapper.py +++ b/test/orm/test_mapper.py @@ -805,6 +805,26 @@ class MapperTest(_fixtures.FixtureTest): eq_(u.name, 'foo') eq_(assert_col, [('get', 'jack'), ('set', 'foo'), ('get', 'foo')]) + @testing.resolve_artifact_names + def test_synonym_map_column_conflict(self): + assert_raises( + sa.exc.ArgumentError, + mapper, + User, users, properties=util.OrderedDict([ + ('_user_id', users.c.id), + ('id', synonym('_user_id', map_column=True)), + ]) + ) + + assert_raises( + sa.exc.ArgumentError, + mapper, + User, users, properties=util.OrderedDict([ + ('id', synonym('_user_id', map_column=True)), + ('_user_id', users.c.id), + ]) + ) + @testing.resolve_artifact_names def test_comparable(self): class extendedproperty(property): -- 2.47.3