From: Mike Bayer Date: Fri, 26 Nov 2010 21:28:41 +0000 (-0500) Subject: - merge mapper simpler compile branch, [ticket:1966] X-Git-Tag: rel_0_7b1~239 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f1e4718f79564e53e2cee8ba51bbdf8af84bc903;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - merge mapper simpler compile branch, [ticket:1966] --- f1e4718f79564e53e2cee8ba51bbdf8af84bc903 diff --cc lib/sqlalchemy/orm/mapper.py index 32eb8f6433,ee41be00b0..0721f7376e --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@@ -46,8 -46,11 +46,8 @@@ _new_mappers = Fals _already_compiling = False _none_set = frozenset([None]) - _memoized_compiled_property = util.group_expirable_memoized_property() + _memoized_configured_property = util.group_expirable_memoized_property() -# a list of MapperExtensions that will be installed in all mappers by default -global_extensions = [] - # a constant returned by _get_attr_by_column to indicate # this mapper is not handling an attribute for a particular # column @@@ -412,14 -412,36 +412,31 @@@ class Mapper(object) for name in method.__sa_validators__: self._validators[name] = method - if 'reconstruct_instance' in self.extension: - def reconstruct(instance): - self.extension.reconstruct_instance(self, instance) - event_registry.add_listener('on_load', reconstruct) - manager.info[_INSTRUMENTOR] = self - + + @util.deprecated("0.7", message=":meth:`.Mapper.compile` " + "is replaced by :func:`.configure_mappers`") + def compile(self): + """Initialize the inter-mapper relationships of all mappers that + have been constructed thus far. + + """ + configure_mappers() + return self + + + @property + @util.deprecated("0.7", message=":attr:`.Mapper.compiled` " + "is replaced by :attr:`.Mapper.configured`") + def compiled(self): + return self.configured + def dispose(self): # Disable any attribute-based compilation. - self.compiled = True + self.configured = True - if hasattr(self, '_compile_failed'): - del self._compile_failed + if hasattr(self, '_configure_failed'): + del self._configure_failed if not self.non_primary and \ self.class_manager.is_mapped and \ @@@ -922,22 -864,36 +885,23 @@@ def has_property(self, key): return key in self._props - def get_property(self, key, - resolve_synonyms=False, - raiseerr=True, _compile_mappers=True): - - """return a :class:`.MapperProperty` associated with the given key. + def get_property(self, key, _compile_mappers=True): + """return a MapperProperty associated with the given key. - resolve_synonyms=False and raiseerr=False are deprecated. + Calls getattr() against the mapped class itself, so that class-level + proxies will be resolved to the underlying property, if any. """ - + - if _compile_mappers and not self.compiled: - self.compile() + if _compile_mappers and _new_mappers: + configure_mappers() - - if not resolve_synonyms: - prop = self._props.get(key, None) - if prop is None and raiseerr: - raise sa_exc.InvalidRequestError( - "Mapper '%s' has no property '%s'" % - (self, key)) - return prop - else: - try: - return getattr(self.class_, key).property - except AttributeError: - if raiseerr: - raise sa_exc.InvalidRequestError( - "Mapper '%s' has no property '%s'" % (self, key)) - else: - return None - ++ + try: + return getattr(self.class_, key).property + except AttributeError: + raise sa_exc.InvalidRequestError( + "Mapper '%s' has no property '%s'" % (self, key)) + @util.deprecated('0.6.4', 'Call to deprecated function mapper._get_col_to_pr' 'op(). Use mapper.get_property_by_column()') @@@ -2395,30 -2411,35 +2413,30 @@@ def validates(*names) return fn return wrap -def _event_on_init(state, instance, args, kwargs): - """Trigger mapper compilation and run init_instance hooks.""" - +def _event_on_load(state): instrumenting_mapper = state.manager.info[_INSTRUMENTOR] - if _new_mappers: - configure_mappers() - if 'init_instance' in instrumenting_mapper.extension: - instrumenting_mapper.extension.init_instance( - instrumenting_mapper, instrumenting_mapper.class_, - state.manager.events.original_init, - instance, args, kwargs) - -def _event_on_init_failure(state, instance, args, kwargs): - """Run init_failed hooks.""" + if instrumenting_mapper._reconstructor: + instrumenting_mapper._reconstructor(state.obj()) + +def _event_on_init(state, args, kwargs): + """Trigger mapper compilation and run init_instance hooks.""" - instrumenting_mapper = state.manager.info[_INSTRUMENTOR] - if 'init_failed' in instrumenting_mapper.extension: - util.warn_exception( - instrumenting_mapper.extension.init_failed, - instrumenting_mapper, instrumenting_mapper.class_, - state.manager.events.original_init, instance, args, kwargs) + instrumenting_mapper = state.manager.info.get(_INSTRUMENTOR) + if instrumenting_mapper: - # compile() always compiles all mappers - instrumenting_mapper.compile() ++ if _new_mappers: ++ configure_mappers() + + if instrumenting_mapper._set_polymorphic_identity: + instrumenting_mapper._set_polymorphic_identity(state) -def _event_on_resurrect(state, instance): +def _event_on_resurrect(state): # re-populate the primary key elements # of the dict based on the mapping. - instrumenting_mapper = state.manager.info[_INSTRUMENTOR] - for col, val in zip(instrumenting_mapper.primary_key, state.key[1]): - instrumenting_mapper._set_state_attr_by_column( - state, state.dict, col, val) + instrumenting_mapper = state.manager.info.get(_INSTRUMENTOR) + if instrumenting_mapper: + for col, val in zip(instrumenting_mapper.primary_key, state.key[1]): + instrumenting_mapper._set_state_attr_by_column( + state, state.dict, col, val) def _sort_states(states): diff --cc lib/sqlalchemy/orm/state.py index 3e977a4c9c,4e67f934c7..059788bac1 --- a/lib/sqlalchemy/orm/state.py +++ b/lib/sqlalchemy/orm/state.py @@@ -8,12 -1,13 +8,14 @@@ defines a large part of the ORM's inter from sqlalchemy.util import EMPTY_SET import weakref from sqlalchemy import util - -from sqlalchemy.orm.attributes import PASSIVE_NO_RESULT, PASSIVE_OFF, \ - NEVER_SET, NO_VALUE, manager_of_class, \ - ATTR_WAS_SET -from sqlalchemy.orm import attributes, exc as orm_exc, interfaces, configure_mappers ++ +from sqlalchemy.orm import exc as orm_exc, attributes, interfaces +from sqlalchemy.orm.attributes import PASSIVE_OFF, PASSIVE_NO_RESULT, \ + PASSIVE_NO_FETCH, NEVER_SET, ATTR_WAS_SET, NO_VALUE + ++mapperlib = util.importlater("sqlalchemy.orm", "mapperlib") + import sys -attributes.state = sys.modules['sqlalchemy.orm.state'] class InstanceState(object): """tracks state information at the instance level.""" @@@ -166,8 -164,8 +168,8 @@@ "Cannot deserialize object of type %r - no mapper() has" " been configured for this class within the current Python process!" % self.class_) - elif manager.is_mapped and not manager.mapper.compiled: - manager.mapper.compile() + elif manager.is_mapped and not manager.mapper.configured: - configure_mappers() ++ mapperlib.configure_mappers() self.committed_state = state.get('committed_state', {}) self.pending = state.get('pending', {}) diff --cc test/ext/test_declarative.py index 88fd5b9763,45d01ba740..32173cdc1c --- a/test/ext/test_declarative.py +++ b/test/ext/test_declarative.py @@@ -299,38 -299,6 +299,38 @@@ class DeclarativeTest(DeclarativeTestBa assert class_mapper(User).get_property('props').secondary \ is user_to_prop + def test_string_dependency_resolution_schemas(self): + Base = decl.declarative_base() + + class User(Base): + + __tablename__ = 'users' + __table_args__ = {'schema':'fooschema'} + + id = Column(Integer, primary_key=True) + name = Column(String(50)) + props = relationship('Prop', secondary='fooschema.user_to_prop', + primaryjoin='User.id==fooschema.user_to_prop.c.user_id', + secondaryjoin='fooschema.user_to_prop.c.prop_id==Prop.id', + backref='users') + + class Prop(Base): + + __tablename__ = 'props' + __table_args__ = {'schema':'fooschema'} + + id = Column(Integer, primary_key=True) + name = Column(String(50)) + + user_to_prop = Table('user_to_prop', Base.metadata, + Column('user_id', Integer, ForeignKey('fooschema.users.id')), + Column('prop_id',Integer, ForeignKey('fooschema.props.id')), + schema='fooschema') - compile_mappers() ++ configure_mappers() + + assert class_mapper(User).get_property('props').secondary \ + is user_to_prop + def test_uncompiled_attributes_in_relationship(self): class Address(Base, ComparableEntity): @@@ -1469,43 -1430,8 +1469,43 @@@ class DeclarativeInheritanceTest(Declar related_child1 = Column('c1', Integer) __mapper_args__ = dict(polymorphic_identity='child2') - sa.orm.compile_mappers() # no exceptions here + sa.orm.configure_mappers() # no exceptions here + def test_foreign_keys_with_col(self): + """Test that foreign keys that reference a literal 'id' subclass + 'id' attribute behave intuitively. + + See [ticket:1892]. + + """ + class Booking(Base): + __tablename__ = 'booking' + id = Column(Integer, primary_key=True) + + class PlanBooking(Booking): + __tablename__ = 'plan_booking' + id = Column(Integer, ForeignKey(Booking.id), + primary_key=True) + + # referencing PlanBooking.id gives us the column + # on plan_booking, not booking + class FeatureBooking(Booking): + __tablename__ = 'feature_booking' + id = Column(Integer, ForeignKey(Booking.id), + primary_key=True) + plan_booking_id = Column(Integer, + ForeignKey(PlanBooking.id)) + + plan_booking = relationship(PlanBooking, + backref='feature_bookings') + + assert FeatureBooking.__table__.c.plan_booking_id.\ + references(PlanBooking.__table__.c.id) + + assert FeatureBooking.__table__.c.id.\ + references(Booking.__table__.c.id) + + def test_single_colsonbase(self): """test single inheritance where all the columns are on the base class.""" diff --cc test/orm/test_mapper.py index 8360d54bfa,eabb710ff9..7d12a8bab5 --- a/test/orm/test_mapper.py +++ b/test/orm/test_mapper.py @@@ -6,13 -6,8 +6,13 @@@ from sqlalchemy.test import testing, pi from sqlalchemy import MetaData, Integer, String, ForeignKey, func, util from sqlalchemy.test.schema import Table, Column from sqlalchemy.engine import default -from sqlalchemy.orm import mapper, relationship, backref, create_session, class_mapper, configure_mappers, reconstructor, validates, aliased -from sqlalchemy.orm import defer, deferred, synonym, attributes, column_property, composite, relationship, dynamic_loader, comparable_property,AttributeExtension +from sqlalchemy.orm import mapper, relationship, backref, \ - create_session, class_mapper, compile_mappers, reconstructor, \ ++ create_session, class_mapper, configure_mappers, reconstructor, \ + validates, aliased, Mapper +from sqlalchemy.orm import defer, deferred, synonym, attributes, \ + column_property, composite, relationship, dynamic_loader, \ + comparable_property, AttributeExtension +from sqlalchemy.orm.instrumentation import ClassManager from sqlalchemy.test.testing import eq_, AssertsCompiledSQL from test.orm import _base, _fixtures from sqlalchemy.test.assertsql import AllOf, CompiledSQL @@@ -230,20 -269,52 +231,31 @@@ class MapperTest(_fixtures.FixtureTest) mapper(Foo, addresses, inherits=User) assert getattr(Foo().__class__, 'name').impl is not None - @testing.resolve_artifact_names - def test_extension_collection_frozen(self): - class Foo(User):pass - m = mapper(User, users) - mapper(Order, orders) - configure_mappers() - mapper(Foo, addresses, inherits=User) - ext_list = [AttributeExtension()] - m.add_property('somename', column_property(users.c.name, extension=ext_list)) - m.add_property('orders', relationship(Order, extension=ext_list, backref='user')) - assert len(ext_list) == 1 - - assert Foo.orders.impl.extensions is User.orders.impl.extensions - assert Foo.orders.impl.extensions is not ext_list - - configure_mappers() - assert len(User.somename.impl.extensions) == 1 - assert len(Foo.somename.impl.extensions) == 1 - assert len(Foo.orders.impl.extensions) == 3 - assert len(User.orders.impl.extensions) == 3 - @testing.resolve_artifact_names - def test_compile_on_get_props_1(self): + def test_configure_on_get_props_1(self): m =mapper(User, users) - assert not m.compiled + assert not m.configured assert list(m.iterate_properties) - assert m.compiled + assert m.configured @testing.resolve_artifact_names - def test_compile_on_get_props_2(self): + def test_configure_on_get_props_2(self): m= mapper(User, users) - assert not m.compiled + assert not m.configured assert m.get_property('name') - assert m.compiled + assert m.configured + + @testing.resolve_artifact_names + def test_configure_on_get_props_3(self): + m= mapper(User, users) + assert not m.configured + configure_mappers() + + m2 = mapper(Address, addresses, properties={ + 'user':relationship(User, backref='addresses') + }) + assert m.get_property('addresses') @testing.resolve_artifact_names def test_add_property(self): @@@ -1183,10 -1249,10 +1196,10 @@@ class DocumentTest(testing.TestBase) backref=backref('foo',doc='foo relationship') ), 'foober':column_property(t1.c.col3, doc='alternate data col'), - 'hoho':synonym(t1.c.col4, doc="syn of col4") + 'hoho':synonym("col4", doc="syn of col4") }) mapper(Bar, t2) - compile_mappers() + configure_mappers() eq_(Foo.col1.__doc__, "primary key column") eq_(Foo.col2.__doc__, "data col") eq_(Foo.col5.__doc__, None)