summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/query_caching/query_caching.py10
-rw-r--r--lib/sqlalchemy/orm/attributes.py23
-rw-r--r--lib/sqlalchemy/orm/interfaces.py64
-rw-r--r--lib/sqlalchemy/orm/mapper.py56
-rw-r--r--lib/sqlalchemy/orm/query.py34
-rw-r--r--lib/sqlalchemy/orm/session.py42
-rw-r--r--test/orm/alltests.py2
-rw-r--r--test/orm/attributes.py2
-rw-r--r--test/orm/bind.py2
-rw-r--r--test/orm/deprecations.py38
-rw-r--r--test/orm/expire.py43
-rw-r--r--test/orm/extendedattr.py13
-rw-r--r--test/orm/mapper.py72
-rw-r--r--test/orm/naturalpks.py38
-rw-r--r--test/orm/query.py16
-rw-r--r--test/orm/session.py6
-rw-r--r--test/orm/transaction.py8
-rw-r--r--test/orm/unitofwork.py16
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