diff options
-rw-r--r-- | examples/query_caching/query_caching.py | 10 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/attributes.py | 23 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/interfaces.py | 64 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/mapper.py | 56 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/query.py | 34 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/session.py | 42 | ||||
-rw-r--r-- | test/orm/alltests.py | 2 | ||||
-rw-r--r-- | test/orm/attributes.py | 2 | ||||
-rw-r--r-- | test/orm/bind.py | 2 | ||||
-rw-r--r-- | test/orm/deprecations.py | 38 | ||||
-rw-r--r-- | test/orm/expire.py | 43 | ||||
-rw-r--r-- | test/orm/extendedattr.py | 13 | ||||
-rw-r--r-- | test/orm/mapper.py | 72 | ||||
-rw-r--r-- | test/orm/naturalpks.py | 38 | ||||
-rw-r--r-- | test/orm/query.py | 16 | ||||
-rw-r--r-- | test/orm/session.py | 6 | ||||
-rw-r--r-- | test/orm/transaction.py | 8 | ||||
-rw-r--r-- | test/orm/unitofwork.py | 16 |
18 files changed, 214 insertions, 271 deletions
diff --git a/examples/query_caching/query_caching.py b/examples/query_caching/query_caching.py index fb250acca..70dc6683c 100644 --- a/examples/query_caching/query_caching.py +++ b/examples/query_caching/query_caching.py @@ -30,21 +30,13 @@ class CachingQuery(Query): else: return Query.__iter__(self) -# currently the easiest way to get a custom Query class in the mix is just -# to subclass Session. A decorated sessionmaker() would probably work too. -class CacheableSession(Session): - def __init__(self, **kwargs): - super(CacheableSession, self).__init__(**kwargs) - self._query_cls = CachingQuery - - # example usage if __name__ == '__main__': from sqlalchemy import Column, create_engine, Integer, String from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.declarative import declarative_base - Session = sessionmaker(class_=CacheableSession) + Session = sessionmaker(query_cls=CachingQuery) Base = declarative_base(engine=create_engine('sqlite://', echo=True)) diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 295535b6d..2dbef2497 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -991,7 +991,7 @@ class Events(object): fn(*args, **kwargs) def add_listener(self, event, listener): - # not thread safe... problem? + # not thread safe... problem? mb: nope bucket = getattr(self, event) if bucket == (): setattr(self, event, [listener]) @@ -1006,8 +1006,8 @@ class Events(object): class ClassManager(dict): """tracks state information at the class level.""" - MANAGER_ATTR = '_fooclass_manager' - STATE_ATTR = '_foostate' + MANAGER_ATTR = '_sa_class_manager' + STATE_ATTR = '_sa_instance_state' event_registry_factory = Events instance_state_factory = InstanceState @@ -1028,6 +1028,10 @@ class ClassManager(dict): self._instantiable = False self.events = self.event_registry_factory() + for meth in class_.__dict__.values(): + if hasattr(meth, '_sa_reconstitute'): + self.events.add_listener('on_load', meth) + def instantiable(self, boolean): # experiment, probably won't stay in this form assert boolean ^ self._instantiable, (boolean, self._instantiable) @@ -1438,6 +1442,19 @@ def del_attribute(instance, key): def is_instrumented(instance, key): return manager_of_class(instance.__class__).is_instrumented(key, search=True) +def on_reconstitute(fn): + """Decorate a method as the 'reconstitute' hook. + + This method will be called based on the 'on_load' event hook. + + Note that when using ORM mappers, this method is equivalent + to MapperExtension.on_reconstitute(). + + """ + fn._sa_reconstitute = True + return fn + + class InstrumentationRegistry(object): """Private instrumentation registration singleton.""" diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py index eb333dfd2..4cfc9462a 100644 --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@ -72,32 +72,15 @@ class MapperExtension(object): def init_failed(self, mapper, class_, oldinit, instance, args, kwargs): return EXT_CONTINUE - def load(self, query, *args, **kwargs): - """Override the `load` method of the Query object. - - The return value of this method is used as the result of - ``query.load()`` if the value is anything other than EXT_CONTINUE. - """ - - return EXT_CONTINUE - - def get(self, query, *args, **kwargs): - """Override the `get` method of the Query object. - - The return value of this method is used as the result of - ``query.get()`` if the value is anything other than EXT_CONTINUE. - """ - - return EXT_CONTINUE - def translate_row(self, mapper, context, row): """Perform pre-processing on the given result row and return a new row instance. - This is called as the very first step in the ``_instance()`` - method. + This is called when the mapper first receives a row, before + the object identity or the instance itself has been derived + from that row. + """ - return EXT_CONTINUE def create_instance(self, mapper, selectcontext, row, class_): @@ -121,8 +104,8 @@ class MapperExtension(object): return value A new object instance, or EXT_CONTINUE - """ + """ return EXT_CONTINUE def append_result(self, mapper, selectcontext, row, instance, result, **flags): @@ -152,25 +135,50 @@ class MapperExtension(object): \**flags extra information about the row, same as criterion in - `create_row_processor()` method of [sqlalchemy.orm.interfaces#MapperProperty] + ``create_row_processor()`` method of [sqlalchemy.orm.interfaces#MapperProperty] """ return EXT_CONTINUE def populate_instance(self, mapper, selectcontext, row, instance, **flags): - """Receive a newly-created instance before that instance has + """Receive an instance before that instance has its attributes populated. + + This usually corresponds to a newly loaded instance but may + also correspond to an already-loaded instance which has + unloaded attributes to be populated. The method may be + called many times for a single instance, as multiple + result rows are used to populate eagerly loaded collections. - The normal population of attributes is according to each - attribute's corresponding MapperProperty (which includes - column-based attributes as well as relationships to other - classes). If this method returns EXT_CONTINUE, instance + If this method returns EXT_CONTINUE, instance population will proceed normally. If any other value or None is returned, instance population will not proceed, giving this extension an opportunity to populate the instance itself, if desired. + + As of 0.5, most usages of this hook are obsolete. + For a generic "object has been newly created from a row" hook, + use ``on_reconstitute()``, or the @attributes.on_reconstitute + decorator. + """ + return EXT_CONTINUE + def on_reconstitute(self, mapper, instance): + """Receive an object instance after it has been created via + ``__new__()``, and after initial attribute population has + occurred. + + This typicically occurs when the instance is created based + on incoming result rows, and is only called once for that + instance's lifetime. + + Note that during a result-row load, this method is called upon + the first row received for this instance; therefore, if eager loaders + are to further populate collections on the instance, those will + *not* have been completely loaded as of yet. + + """ return EXT_CONTINUE def before_insert(self, mapper, connection, instance): diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 79e96dcaf..3139ac507 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -332,8 +332,6 @@ class Mapper(object): if not self.non_primary and self.entity_name in mappers: del mappers[self.entity_name] if not mappers and manager.info.get(_INSTRUMENTOR, False): - for legacy in _legacy_descriptors.keys(): - manager.uninstall_member(legacy) manager.events.remove_listener('on_init', _event_on_init) manager.events.remove_listener('on_init_failure', _event_on_init_failure) @@ -815,9 +813,11 @@ class Mapper(object): event_registry = manager.events event_registry.add_listener('on_init', _event_on_init) event_registry.add_listener('on_init_failure', _event_on_init_failure) + if 'on_reconstitute' in self.extension.methods: + def reconstitute(instance): + self.extension.on_reconstitute(self, instance) + event_registry.add_listener('on_load', reconstitute) - for key, impl in _legacy_descriptors.items(): - manager.install_member(key, impl) manager.info[_INSTRUMENTOR] = self @@ -1445,7 +1445,8 @@ class Mapper(object): if instance is EXT_CONTINUE: instance = self.class_manager.new_instance() else: - manager = attributes.manager_for_cls(instance.__class__) + # TODO: don't think theres coverage here + manager = attributes.manager_of_class(instance.__class__) # TODO: if manager is None, raise a friendly error about # returning instances of unmapped types manager.setup_instance(instance) @@ -1488,12 +1489,12 @@ class Mapper(object): if not populate_instance or extension.populate_instance(self, context, row, instance, only_load_props=attrs, instancekey=identitykey, isnew=isnew) is EXT_CONTINUE: populate_state(state, row, isnew, attrs, instancekey=identitykey) - if result is not None and (not append_result or extension.append_result(self, context, row, instance, result, instancekey=identitykey, isnew=isnew) is EXT_CONTINUE): - result.append(instance) - if loaded_instance: state._run_on_load(instance) + if result is not None and (not append_result or extension.append_result(self, context, row, instance, result, instancekey=identitykey, isnew=isnew) is EXT_CONTINUE): + result.append(instance) + return instance return _instance @@ -1572,45 +1573,6 @@ def _event_on_init_failure(state, instance, args, kwargs): instrumenting_mapper, instrumenting_mapper.class_, state.manager.events.original_init, instance, args, kwargs) -def _legacy_descriptors(): - """Build compatibility descriptors mapping legacy to InstanceState. - - These are slated for removal in 0.5. They were never part of the - official public API but were suggested as temporary workarounds in a - number of mailing list posts. Permanent and public solutions for those - needs should be available now. Consult the applicable mailing list - threads for details. - - """ - def _instance_key(self): - state = attributes.instance_state(self) - if state.key is not None: - return state.key - else: - raise AttributeError("_instance_key") - _instance_key = util.deprecated(None, False)(_instance_key) - _instance_key = property(_instance_key) - - def _sa_session_id(self): - state = attributes.instance_state(self) - if state.session_id is not None: - return state.session_id - else: - raise AttributeError("_sa_session_id") - _sa_session_id = util.deprecated(None, False)(_sa_session_id) - _sa_session_id = property(_sa_session_id) - - def _entity_name(self): - state = attributes.instance_state(self) - if state.entity_name is attributes.NO_ENTITY_NAME: - return None - else: - return state.entity_name - _entity_name = util.deprecated(None, False)(_entity_name) - _entity_name = property(_entity_name) - - return dict(locals()) -_legacy_descriptors = _legacy_descriptors() def _load_scalar_attributes(state, attribute_names): mapper = _state_mapper(state) diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 700d24d33..df775a177 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -397,7 +397,7 @@ class Query(object): self._yield_per = count yield_per = _generative()(yield_per) - def get(self, ident, **kwargs): + def get(self, ident): """Return an instance of the object based on the given identifier, or None if not found. The `ident` argument is a scalar or tuple of primary key column values @@ -405,39 +405,12 @@ class Query(object): """ - ret = self._extension_zero().get(self, ident, **kwargs) - if ret is not mapper.EXT_CONTINUE: - return ret - - # convert composite types to individual args - if hasattr(ident, '__composite_values__'): - ident = ident.__composite_values__() - - key = self._only_mapper_zero().identity_key_from_primary_key(ident) - return self._get(key, ident, **kwargs) - - def load(self, ident, raiseerr=True, **kwargs): - """Return an instance of the object based on the given identifier. - - If not found, raises an exception. The method will **remove all - pending changes** to the object already existing in the Session. The - `ident` argument is a scalar or tuple of primary key column values in - the order of the table def's primary key columns. - - """ - ret = self._extension_zero().load(self, ident, **kwargs) - if ret is not mapper.EXT_CONTINUE: - return ret - # convert composite types to individual args if hasattr(ident, '__composite_values__'): ident = ident.__composite_values__() key = self._only_mapper_zero().identity_key_from_primary_key(ident) - instance = self.populate_existing()._get(key, ident, **kwargs) - if instance is None and raiseerr: - raise sa_exc.InvalidRequestError("No instance found for identity %s" % repr(ident)) - return instance + return self._get(key, ident) def query_from_parent(cls, instance, property, **kwargs): """Return a new Query with criterion corresponding to a parent instance. @@ -1148,8 +1121,7 @@ class Query(object): try: state() except orm_exc.ObjectDeletedError: - # TODO: should we expunge ? if so, should we expunge here ? or in mapper._load_scalar_attributes ? - self.session.expunge(instance) + self.session._remove_newly_deleted(state) return None return instance except KeyError: diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 5d6326ac4..a2dbb8e11 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -145,6 +145,10 @@ def sessionmaker(bind=None, class_=None, autoflush=True, autocommit=False, called. This allows each database to roll back the entire transaction, before each transaction is committed. + query_cls + Class which should be used to create new Query objects, as returned + by the ``query()`` method. Defaults to [sqlalchemy.orm.query#Query]. + weak_identity_map When set to the default value of ``False``, a weak-referencing map is used; instances which are not externally referenced will be garbage @@ -505,13 +509,13 @@ class Session(object): public_methods = ( '__contains__', '__iter__', 'add', 'add_all', 'begin', 'begin_nested', 'clear', 'close', 'commit', 'connection', 'delete', 'execute', 'expire', - 'expire_all', 'expunge', 'flush', 'get', 'get_bind', 'is_modified', - 'load', 'merge', 'query', 'refresh', 'rollback', 'save', + 'expire_all', 'expunge', 'flush', 'get_bind', 'is_modified', + 'merge', 'query', 'refresh', 'rollback', 'save', 'save_or_update', 'scalar', 'update') def __init__(self, bind=None, autoflush=True, autoexpire=True, autocommit=False, twophase=False, echo_uow=False, - weak_identity_map=True, binds=None, extension=None): + weak_identity_map=True, binds=None, extension=None, query_cls=query.Query): """Construct a new Session. Arguments to ``Session`` are described using the @@ -536,7 +540,7 @@ class Session(object): self.autoexpire = autoexpire self.twophase = twophase self.extension = extension - self._query_cls = query.Query + self._query_cls = query_cls self._mapper_flush_opts = {} if binds is not None: @@ -896,34 +900,6 @@ class Session(object): for state in states: state.commit_all() - def get(self, class_, ident, entity_name=None): - """Return the instance of class with ident or None. - - The `ident` argument is a scalar or tuple of primary key column values - in the order of the table def's primary key columns. - - The `entity_name` keyword argument may also be specified which further - qualifies the underlying Mapper used to perform the query. - - """ - return self.query(_class_to_mapper(class_, entity_name)).get(ident) - - return self.query(class_, entity_name=entity_name).get(ident) - - def load(self, class_, ident, entity_name=None): - """Reset and return the instance of class with ident or raise. - - If not found, raises an exception. The method will **remove all - pending changes** to the object already existing in the ``Session``. - The `ident` argument is a scalar or tuple of primary key columns in - the order of the table def's primary key columns. - - The `entity_name` keyword argument may also be specified which further - qualifies the underlying ``Mapper`` used to perform the query. - - """ - return self.query(_class_to_mapper(class_, entity_name)).load(ident) - def refresh(self, instance, attribute_names=None): """Refresh the attributes on the given instance. @@ -1201,7 +1177,7 @@ class Session(object): self._update_impl(merged_state) new_instance = True else: - merged = self.get(mapper.class_, key[1]) + merged = self.query(mapper.class_).get(key[1]) if merged is None: merged = mapper.class_manager.new_instance() diff --git a/test/orm/alltests.py b/test/orm/alltests.py index 6d6db3a19..bce775f92 100644 --- a/test/orm/alltests.py +++ b/test/orm/alltests.py @@ -6,6 +6,7 @@ import sharding.alltests as sharding def suite(): modules_to_test = ( + 'orm.memusage', 'orm.attributes', 'orm.bind', 'orm.extendedattr', @@ -31,7 +32,6 @@ def suite(): 'orm.association', 'orm.merge', 'orm.pickled', - 'orm.memusage', 'orm.utils', 'orm.cycles', diff --git a/test/orm/attributes.py b/test/orm/attributes.py index 26828ecac..327a3efd3 100644 --- a/test/orm/attributes.py +++ b/test/orm/attributes.py @@ -747,9 +747,7 @@ class HistoryTest(_base.ORMTest): # no lazyload occurs so this allows overwrite operation to proceed f = Foo() eq_(attributes.get_history(attributes.instance_state(f), 'someattr'), ([], [], [])) - print f._foostate.committed_state f.someattr = None - print f._foostate.committed_state, f._foostate.dict eq_(attributes.get_history(attributes.instance_state(f), 'someattr'), ([None], [], [])) f = Foo() diff --git a/test/orm/bind.py b/test/orm/bind.py index 47fba9168..189b864c0 100644 --- a/test/orm/bind.py +++ b/test/orm/bind.py @@ -35,7 +35,7 @@ class BindTest(_base.MappedTest): f = Foo() sess.save(f) sess.flush() - assert sess.get(Foo, f.id) is f + assert sess.query(Foo).get(f.id) is f finally: if hasattr(bind, 'close'): bind.close() diff --git a/test/orm/deprecations.py b/test/orm/deprecations.py index 71d807059..9da3571f9 100644 --- a/test/orm/deprecations.py +++ b/test/orm/deprecations.py @@ -8,7 +8,7 @@ be migrated directly to the wiki, docs, etc. import testenv; testenv.configure_for_tests() from testlib import testing from testlib.sa import Table, Column, Integer, String, ForeignKey, func -from testlib.sa.orm import mapper, relation, create_session +from testlib.sa.orm import mapper, relation, create_session, sessionmaker from orm import _base @@ -90,6 +90,42 @@ class QueryAlternativesTest(_base.MappedTest): ###################################################################### @testing.resolve_artifact_names + def test_override_get(self): + """MapperExtension.get() + + x = session.query.get(5) + + """ + from sqlalchemy.orm.query import Query + cache = {} + class MyQuery(Query): + def get(self, ident, **kwargs): + if ident in cache: + return cache[ident] + else: + x = super(MyQuery, self).get(ident) + cache[ident] = x + return x + + session = sessionmaker(query_cls=MyQuery)() + + ad1 = session.query(Address).get(1) + assert ad1 in cache.values() + + @testing.resolve_artifact_names + def test_load(self): + """x = session.query(Address).load(1) + + x = session.load(Address, 1) + + """ + + session = create_session() + ad1 = session.query(Address).populate_existing().get(1) + assert bool(ad1) + + + @testing.resolve_artifact_names def test_apply_max(self): """Query.apply_max(col) diff --git a/test/orm/expire.py b/test/orm/expire.py index ec137b04c..d7ed0419e 100644 --- a/test/orm/expire.py +++ b/test/orm/expire.py @@ -53,7 +53,7 @@ class ExpireTest(_fixtures.FixtureTest): def test_persistence_check(self): mapper(User, users) s = create_session() - u = s.get(User, 7) + u = s.query(User).get(7) s.clear() self.assertRaisesMessage(sa.exc.InvalidRequestError, r"is not persistent within this Session", s.expire, u) @@ -61,26 +61,26 @@ class ExpireTest(_fixtures.FixtureTest): @testing.resolve_artifact_names def test_get_refreshes(self): mapper(User, users) - s = create_session() - u = s.get(User, 10) + s = create_session(autocommit=False) + u = s.query(User).get(10) s.expire_all() def go(): - u = s.get(User, 10) # get() refreshes + u = s.query(User).get(10) # get() refreshes self.assert_sql_count(testing.db, go, 1) def go(): self.assertEquals(u.name, 'chuck') # attributes unexpired self.assert_sql_count(testing.db, go, 0) def go(): - u = s.get(User, 10) # expire flag reset, so not expired + u = s.query(User).get(10) # expire flag reset, so not expired self.assert_sql_count(testing.db, go, 0) s.expire_all() - users.delete().where(User.id==10).execute() + s.execute(users.delete().where(User.id==10)) - # object is gone, get() returns None + # object is gone, get() returns None, removes u from session assert u in s - assert s.get(User, 10) is None + assert s.query(User).get(10) is None assert u not in s # and expunges # add it back @@ -88,16 +88,27 @@ class ExpireTest(_fixtures.FixtureTest): # nope, raises ObjectDeletedError self.assertRaises(sa.orm.exc.ObjectDeletedError, getattr, u, 'name') + # do a get()/remove u from session again + assert s.query(User).get(10) is None + assert u not in s + + s.rollback() + + assert u in s + # but now its back, rollback has occured, the _remove_newly_deleted + # is reverted + self.assertEquals(u.name, 'chuck') + @testing.resolve_artifact_names def test_refresh_cancels_expire(self): mapper(User, users) s = create_session() - u = s.get(User, 7) + u = s.query(User).get(7) s.expire(u) s.refresh(u) def go(): - u = s.get(User, 7) + u = s.query(User).get(7) self.assertEquals(u.name, 'jack') self.assert_sql_count(testing.db, go, 0) @@ -221,7 +232,7 @@ class ExpireTest(_fixtures.FixtureTest): }) mapper(Address, addresses) s = create_session() - u = s.get(User, 8) + u = s.query(User).get(8) assert u.addresses[0].email_address == 'ed@wood.com' u.addresses[0].email_address = 'someotheraddress' @@ -693,7 +704,7 @@ class RefreshTest(_fixtures.FixtureTest): 'addresses':relation(mapper(Address, addresses), backref='user') }) s = create_session() - u = s.get(User, 7) + u = s.query(User).get(7) u.name = 'foo' a = Address() assert sa.orm.object_session(a) is None @@ -730,7 +741,7 @@ class RefreshTest(_fixtures.FixtureTest): def test_persistence_check(self): mapper(User, users) s = create_session() - u = s.get(User, 7) + u = s.query(User).get(7) s.clear() self.assertRaisesMessage(sa.exc.InvalidRequestError, r"is not persistent within this Session", lambda: s.refresh(u)) @@ -738,7 +749,7 @@ class RefreshTest(_fixtures.FixtureTest): def test_refresh_expired(self): mapper(User, users) s = create_session() - u = s.get(User, 7) + u = s.query(User).get(7) s.expire(u) assert 'name' not in u.__dict__ s.refresh(u) @@ -767,13 +778,13 @@ class RefreshTest(_fixtures.FixtureTest): }) s = create_session() - u = s.get(User, 8) + u = s.query(User).get(8) assert len(u.addresses) == 3 s.refresh(u) assert len(u.addresses) == 3 s = create_session() - u = s.get(User, 8) + u = s.query(User).get(8) assert len(u.addresses) == 3 s.expire(u) assert len(u.addresses) == 3 diff --git a/test/orm/extendedattr.py b/test/orm/extendedattr.py index 4d0c41f31..a32e57294 100644 --- a/test/orm/extendedattr.py +++ b/test/orm/extendedattr.py @@ -298,6 +298,19 @@ class UserDefinedExtensionTest(_base.ORMTest): self.assertRaises((AttributeError, KeyError), attributes.instance_state, None) +class ReconstituteTest(testing.TestBase): + def test_on_reconstitute(self): + recon = [] + class MyClass(object): + @attributes.on_reconstitute + def recon(self): + recon.append('go') + + attributes.register_class(MyClass) + m = attributes.manager_of_class(MyClass).new_instance() + s = attributes.instance_state(m) + s._run_on_load(m) + assert recon == ['go'] if __name__ == '__main__': testing.main() diff --git a/test/orm/mapper.py b/test/orm/mapper.py index b7010fb2a..9d1a4e576 100644 --- a/test/orm/mapper.py +++ b/test/orm/mapper.py @@ -30,36 +30,6 @@ class MapperTest(_fixtures.FixtureTest): self.assertRaises(NotImplementedError, getattr, sa.orm.class_mapper(User), 'properties') - @testing.uses_deprecated( - 'Call to deprecated function _instance_key', - 'Call to deprecated function _sa_session_id', - 'Call to deprecated function _entity_name') - @testing.resolve_artifact_names - def test_legacy_accessors(self): - u1 = User(name='u1') - assert not hasattr(u1, '_instance_key') - assert not hasattr(u1, '_sa_session_id') - assert not hasattr(u1, '_entity_name') - - mapper(User, users) - u1 = User(name='u2') - assert not hasattr(u1, '_instance_key') - assert not hasattr(u1, '_sa_session_id') - assert u1._entity_name is None - - sess = create_session() - sess.save(u1) - assert not hasattr(u1, '_instance_key') - eq_(u1._sa_session_id, sess.hash_key) - assert u1._entity_name is None - - sess.flush() - eq_(u1._instance_key, - sa.orm.class_mapper(u1).identity_key_from_instance(u1)) - eq_(u1._sa_session_id, sess.hash_key) - assert u1._entity_name is None - sess.delete(u1) - sess.flush() @testing.resolve_artifact_names def test_bad_cascade(self): @@ -74,7 +44,7 @@ class MapperTest(_fixtures.FixtureTest): }) s = create_session() - u = s.get(User, 7) + u = s.query(User).get(7) eq_(u._name, 'jack') eq_(u._id,7) u2 = s.query(User).filter_by(user_name='jack').one() @@ -1481,14 +1451,6 @@ class MapperExtensionTest(_fixtures.FixtureTest): methods.append('init_failed') return sa.orm.EXT_CONTINUE - def load(self, query, *args, **kwargs): - methods.append('load') - return sa.orm.EXT_CONTINUE - - def get(self, query, *args, **kwargs): - methods.append('get') - return sa.orm.EXT_CONTINUE - def translate_row(self, mapper, context, row): methods.append('translate_row') return sa.orm.EXT_CONTINUE @@ -1497,6 +1459,10 @@ class MapperExtensionTest(_fixtures.FixtureTest): methods.append('create_instance') return sa.orm.EXT_CONTINUE + def on_reconstitute(self, mapper, instance): + methods.append('on_reconstitute') + return sa.orm.EXT_CONTINUE + def append_result(self, mapper, selectcontext, row, instance, result, **flags): methods.append('append_result') return sa.orm.EXT_CONTINUE @@ -1541,7 +1507,7 @@ class MapperExtensionTest(_fixtures.FixtureTest): u = User(name='u1') sess.save(u) sess.flush() - u = sess.query(User).load(u.id) + u = sess.query(User).populate_existing().get(u.id) sess.clear() u = sess.query(User).get(u.id) u.name = 'u1 changed' @@ -1550,9 +1516,9 @@ class MapperExtensionTest(_fixtures.FixtureTest): sess.flush() eq_(methods, ['instrument_class', 'init_instance', 'before_insert', - 'after_insert', 'load', 'translate_row', 'populate_instance', - 'append_result', 'get', 'translate_row', 'create_instance', - 'populate_instance', 'append_result', 'before_update', + 'after_insert', 'translate_row', 'populate_instance', + 'append_result', 'translate_row', 'create_instance', + 'populate_instance', 'on_reconstitute', 'append_result', 'before_update', 'after_update', 'before_delete', 'after_delete']) @testing.resolve_artifact_names @@ -1569,7 +1535,7 @@ class MapperExtensionTest(_fixtures.FixtureTest): am = AdminUser(name='au1', email_address='au1@e1') sess.save(am) sess.flush() - am = sess.query(AdminUser).load(am.id) + am = sess.query(AdminUser).populate_existing().get(am.id) sess.clear() am = sess.query(AdminUser).get(am.id) am.name = 'au1 changed' @@ -1578,9 +1544,9 @@ class MapperExtensionTest(_fixtures.FixtureTest): sess.flush() eq_(methods, ['instrument_class', 'instrument_class', 'init_instance', - 'before_insert', 'after_insert', 'load', 'translate_row', - 'populate_instance', 'append_result', 'get', 'translate_row', - 'create_instance', 'populate_instance', 'append_result', + 'before_insert', 'after_insert', 'translate_row', + 'populate_instance', 'append_result', 'translate_row', + 'create_instance', 'populate_instance', 'on_reconstitute', 'append_result', 'before_update', 'after_update', 'before_delete', 'after_delete']) @testing.resolve_artifact_names @@ -1626,7 +1592,7 @@ class MapperExtensionTest(_fixtures.FixtureTest): am = AdminUser(name="au1", email_address="au1@e1") sess.save(am) sess.flush() - am = sess.query(AdminUser).load(am.id) + am = sess.query(AdminUser).populate_existing().get(am.id) sess.clear() am = sess.query(AdminUser).get(am.id) am.name = 'au1 changed' @@ -1635,9 +1601,9 @@ class MapperExtensionTest(_fixtures.FixtureTest): sess.flush() eq_(methods, ['instrument_class', 'instrument_class', 'init_instance', - 'before_insert', 'after_insert', 'load', 'translate_row', - 'populate_instance', 'append_result', 'get', 'translate_row', - 'create_instance', 'populate_instance', 'append_result', + 'before_insert', 'after_insert', 'translate_row', + 'populate_instance', 'append_result', 'translate_row', + 'create_instance', 'populate_instance', 'on_reconstitute', 'append_result', 'before_update', 'after_update', 'before_delete', 'after_delete']) @testing.resolve_artifact_names @@ -1921,7 +1887,7 @@ class ScalarRequirementsTest(_base.MappedTest): session.flush() session.clear() - f1 = session.get(Foo, f1.id) + f1 = session.query(Foo).get(f1.id) eq_(f1.data.data, '12345') f2 = Foo(data=pickleable.BrokenComparable('abc')) @@ -1930,7 +1896,7 @@ class ScalarRequirementsTest(_base.MappedTest): session.flush() session.clear() - f2 = session.get(Foo, f2.id) + f2 = session.query(Foo).get(f2.id) eq_(f2.data.data, 'abc') diff --git a/test/orm/naturalpks.py b/test/orm/naturalpks.py index 1dc47f1cf..d3fd5db29 100644 --- a/test/orm/naturalpks.py +++ b/test/orm/naturalpks.py @@ -47,16 +47,16 @@ class NaturalPKTest(_base.MappedTest): sess.add(u1) sess.flush() - assert sess.get(User, 'jack') is u1 + assert sess.query(User).get('jack') is u1 u1.username = 'ed' sess.flush() def go(): - assert sess.get(User, 'ed') is u1 + assert sess.query(User).get('ed') is u1 self.assert_sql_count(testing.db, go, 0) - assert sess.get(User, 'jack') is None + assert sess.query(User).get('jack') is None sess.clear() u1 = sess.query(User).get('ed') @@ -71,7 +71,7 @@ class NaturalPKTest(_base.MappedTest): sess.add(u1) sess.flush() - assert sess.get(User, 'jack') is u1 + assert sess.query(User).get('jack') is u1 users.update(values={User.username:'jack'}).execute(username='ed') @@ -82,8 +82,8 @@ class NaturalPKTest(_base.MappedTest): self.assertRaises(sa.orm.exc.ObjectDeletedError, getattr, u1, 'username') sess.clear() - assert sess.get(User, 'jack') is None - assert sess.get(User, 'ed').fullname == 'jack' + assert sess.query(User).get('jack') is None + assert sess.query(User).get('ed').fullname == 'jack' @testing.fails_on('mysql') @testing.fails_on('sqlite') @@ -107,7 +107,7 @@ class NaturalPKTest(_base.MappedTest): sess.add(u1) sess.flush() - assert sess.get(Address, 'jack1') is u1.addresses[0] + assert sess.query(Address).get('jack1') is u1.addresses[0] u1.username = 'ed' sess.flush() @@ -116,7 +116,7 @@ class NaturalPKTest(_base.MappedTest): sess.clear() self.assertEquals([Address(username='ed'), Address(username='ed')], sess.query(Address).all()) - u1 = sess.get(User, 'ed') + u1 = sess.query(User).get('ed') u1.username = 'jack' def go(): sess.flush() @@ -125,15 +125,15 @@ class NaturalPKTest(_base.MappedTest): else: self.assert_sql_count(testing.db, go, 1) # test passive_updates=True; update user sess.clear() - assert User(username='jack', addresses=[Address(username='jack'), Address(username='jack')]) == sess.get(User, 'jack') + assert User(username='jack', addresses=[Address(username='jack'), Address(username='jack')]) == sess.query(User).get('jack') - u1 = sess.get(User, 'jack') + u1 = sess.query(User).get('jack') u1.addresses = [] u1.username = 'fred' sess.flush() sess.clear() - assert sess.get(Address, 'jack1').username is None - u1 = sess.get(User, 'fred') + assert sess.query(Address).get('jack1').username is None + u1 = sess.query(User).get('fred') self.assertEquals(User(username='fred', fullname='jack'), u1) @testing.fails_on('sqlite') @@ -220,7 +220,7 @@ class NaturalPKTest(_base.MappedTest): sess.clear() self.assertEquals([Address(username='ed'), Address(username='ed')], sess.query(Address).all()) - u1 = sess.get(User, 'ed') + u1 = sess.query(User).get('ed') assert len(u1.addresses) == 2 # load addresses u1.username = 'fred' print "--------------------------------" @@ -366,7 +366,7 @@ class NonPKCascadeTest(_base.MappedTest): self.assertEquals(sa.select([addresses.c.username]).execute().fetchall(), [('jack',), ('jack',)]) - assert sess.get(Address, a1.id) is u1.addresses[0] + assert sess.query(Address).get(a1.id) is u1.addresses[0] u1.username = 'ed' sess.flush() @@ -376,7 +376,7 @@ class NonPKCascadeTest(_base.MappedTest): sess.clear() self.assertEquals([Address(username='ed'), Address(username='ed')], sess.query(Address).all()) - u1 = sess.get(User, u1.id) + u1 = sess.query(User).get(u1.id) u1.username = 'jack' def go(): sess.flush() @@ -385,20 +385,20 @@ class NonPKCascadeTest(_base.MappedTest): else: self.assert_sql_count(testing.db, go, 1) # test passive_updates=True; update user sess.clear() - assert User(username='jack', addresses=[Address(username='jack'), Address(username='jack')]) == sess.get(User, u1.id) + assert User(username='jack', addresses=[Address(username='jack'), Address(username='jack')]) == sess.query(User).get(u1.id) sess.clear() - u1 = sess.get(User, u1.id) + u1 = sess.query(User).get(u1.id) u1.addresses = [] u1.username = 'fred' sess.flush() sess.clear() - a1 = sess.get(Address, a1.id) + a1 = sess.query(Address).get(a1.id) self.assertEquals(a1.username, None) self.assertEquals(sa.select([addresses.c.username]).execute().fetchall(), [(None,), (None,)]) - u1 = sess.get(User, u1.id) + u1 = sess.query(User).get(u1.id) self.assertEquals(User(username='fred', fullname='jack'), u1) diff --git a/test/orm/query.py b/test/orm/query.py index fe70dec52..4cef98a5c 100644 --- a/test/orm/query.py +++ b/test/orm/query.py @@ -77,7 +77,6 @@ class GetTest(QueryTest): q = s.query(User).join('addresses').filter(Address.user_id==8) self.assertRaises(sa_exc.InvalidRequestError, q.get, 7) self.assertRaises(sa_exc.InvalidRequestError, s.query(User).filter(User.id==7).get, 19) - self.assertRaises(sa_exc.InvalidRequestError, s.query(User).filter(User.id==7).load, 19) def test_unique_param_names(self): class SomeUser(object): @@ -97,17 +96,13 @@ class GetTest(QueryTest): def test_load(self): s = create_session() - try: - assert s.query(User).load(19) is None - assert False - except sa_exc.InvalidRequestError: - assert True + assert s.query(User).populate_existing().get(19) is None - u = s.query(User).load(7) - u2 = s.query(User).load(7) + u = s.query(User).populate_existing().get(7) + u2 = s.query(User).populate_existing().get(7) assert u is u2 s.clear() - u2 = s.query(User).load(7) + u2 = s.query(User).populate_existing().get(7) assert u is not u2 u2.name = 'some name' @@ -116,7 +111,7 @@ class GetTest(QueryTest): assert u2 in s.dirty assert a in u2.addresses - s.query(User).load(7) + s.query(User).populate_existing().get(7) assert u2 not in s.dirty assert u2.name =='jack' assert a not in u2.addresses @@ -2202,6 +2197,7 @@ class UpdateDeleteTest(_base.MappedTest): eq_(sess.query(User).order_by(User.id).all(), [jack,jane]) + @testing.fails_on('mysql') @testing.resolve_artifact_names def test_delete_fallback(self): sess = create_session(bind=testing.db, autocommit=False) diff --git a/test/orm/session.py b/test/orm/session.py index 7f4181227..079649aed 100644 --- a/test/orm/session.py +++ b/test/orm/session.py @@ -987,7 +987,7 @@ class SessionInterface(testing.TestBase): # TODO: expand with message body assertions. _class_methods = set(( - 'connection', 'execute', 'get', 'get_bind', 'load', 'scalar')) + 'connection', 'execute', 'get_bind', 'scalar')) def _public_session_methods(self): Session = sa.orm.session.Session @@ -1072,12 +1072,8 @@ class SessionInterface(testing.TestBase): raises_('execute', 'SELECT 1', mapper=user_arg) - raises_('get', user_arg, 1) - raises_('get_bind', mapper=user_arg) - raises_('load', user_arg, 1) - raises_('scalar', 'SELECT 1', mapper=user_arg) eq_(watchdog, self._class_methods, diff --git a/test/orm/transaction.py b/test/orm/transaction.py index cfa11d2cc..a4f5a0592 100644 --- a/test/orm/transaction.py +++ b/test/orm/transaction.py @@ -24,14 +24,14 @@ class FixtureDataTest(TransactionTest): def test_attrs_on_rollback(self): sess = self.session() - u1 = sess.get(User, 7) + u1 = sess.query(User).get(7) u1.name = 'ed' sess.rollback() self.assertEquals(u1.name, 'jack') def test_commit_persistent(self): sess = self.session() - u1 = sess.get(User, 7) + u1 = sess.query(User).get(7) u1.name = 'ed' sess.flush() sess.commit() @@ -39,12 +39,12 @@ class FixtureDataTest(TransactionTest): def test_concurrent_commit_persistent(self): s1 = self.session() - u1 = s1.get(User, 7) + u1 = s1.query(User).get(7) u1.name = 'ed' s1.commit() s2 = self.session() - u2 = s2.get(User, 7) + u2 = s2.query(User).get(7) assert u2.name == 'ed' u2.name = 'will' s2.commit() diff --git a/test/orm/unitofwork.py b/test/orm/unitofwork.py index 7beafb58f..5149d2739 100644 --- a/test/orm/unitofwork.py +++ b/test/orm/unitofwork.py @@ -127,7 +127,7 @@ class VersioningTest(_base.MappedTest): self.assertRaises(sa.orm.exc.ConcurrentModificationError, s1.query(Foo).with_lockmode('read').get, f1s1.id) # reload it - s1.query(Foo).load(f1s1.id) + s1.query(Foo).populate_existing().get(f1s1.id) # now assert version OK s1.query(Foo).with_lockmode('read').get(f1s1.id) @@ -403,7 +403,7 @@ class MutableTypesTest(_base.MappedTest): session.commit() session.clear() - f1 = session.get(Foo, f1.id) + f1 = session.query(Foo).get(f1.id) f1.val = u'hi' self.sql_count_(0, session.commit) @@ -1100,7 +1100,7 @@ class OneToManyTest(_fixtures.FixtureTest): session.flush() session.clear() - u2 = session.get(User, u2.id) + u2 = session.query(User).get(u2.id) eq_(len(u2.addresses), 1) @testing.resolve_artifact_names @@ -1123,7 +1123,7 @@ class OneToManyTest(_fixtures.FixtureTest): session.flush() session.clear() - u2 = session.get(User, u2.id) + u2 = session.query(User).get(u2.id) eq_(len(u2.addresses), 1) @testing.resolve_artifact_names @@ -1222,14 +1222,14 @@ class SaveTest(_fixtures.FixtureTest): session.flush() # assert the first one retreives the same from the identity map - nu = session.get(m, u.id) + nu = session.query(m).get(u.id) assert u is nu # clear out the identity map, so next get forces a SELECT session.clear() # check it again, identity should be different but ids the same - nu = session.get(m, u.id) + nu = session.query(m).get(u.id) assert u is not nu and u.id == nu.id and nu.name == 'savetester' # change first users name and save @@ -1403,7 +1403,7 @@ class SaveTest(_fixtures.FixtureTest): id = m.primary_key_from_instance(u) - u = session.get(User, id) + u = session.query(User).get(id) assert u.name == 'multitester' user_rows = users.select(users.c.id.in_([u.foo_id])).execute().fetchall() @@ -1421,7 +1421,7 @@ class SaveTest(_fixtures.FixtureTest): eq_(address_rows[0].values(), [u.id, u.foo_id, 'lala@hey.com']) session.clear() - u = session.get(User, id) + u = session.query(User).get(id) assert u.name == 'imnew' @testing.resolve_artifact_names |