summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/attributes.py20
-rw-r--r--lib/sqlalchemy/orm/mapper.py8
-rw-r--r--lib/sqlalchemy/orm/query.py106
-rw-r--r--lib/sqlalchemy/orm/session.py2
-rw-r--r--lib/sqlalchemy/orm/strategies.py88
5 files changed, 114 insertions, 110 deletions
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py
index 6b235e527..4d0379503 100644
--- a/lib/sqlalchemy/orm/attributes.py
+++ b/lib/sqlalchemy/orm/attributes.py
@@ -844,7 +844,7 @@ def backref_listeners(attribute, key, uselist):
# present when updating via a backref.
old_state, old_dict = instance_state(oldchild),\
instance_dict(oldchild)
- impl = old_state.get_impl(key)
+ impl = old_state.manager[key].impl
try:
impl.remove(old_state,
old_dict,
@@ -856,7 +856,7 @@ def backref_listeners(attribute, key, uselist):
if child is not None:
child_state, child_dict = instance_state(child),\
instance_dict(child)
- child_state.get_impl(key).append(
+ child_state.manager[key].impl.append(
child_state,
child_dict,
state.obj(),
@@ -867,7 +867,7 @@ def backref_listeners(attribute, key, uselist):
def append(state, child, initiator):
child_state, child_dict = instance_state(child), \
instance_dict(child)
- child_state.get_impl(key).append(
+ child_state.manager[key].impl.append(
child_state,
child_dict,
state.obj(),
@@ -879,7 +879,7 @@ def backref_listeners(attribute, key, uselist):
if child is not None:
child_state, child_dict = instance_state(child),\
instance_dict(child)
- child_state.get_impl(key).remove(
+ child_state.manager[key].impl.remove(
child_state,
child_dict,
state.obj(),
@@ -1077,7 +1077,7 @@ def get_all_pending(state, dict_, key):
"""
- return state.manager.get_impl(key).get_all_pending(state, dict_)
+ return state.manager[key].impl.get_all_pending(state, dict_)
def has_parent(cls, obj, key, optimistic=False):
@@ -1172,7 +1172,7 @@ def init_collection(obj, key):
def init_state_collection(state, dict_, key):
"""Initialize a collection attribute and return the collection adapter."""
- attr = state.get_impl(key)
+ attr = state.manager[key].impl
user_data = attr.initialize(state, dict_)
return attr.get_collection(state, dict_, user_data)
@@ -1192,7 +1192,7 @@ def set_committed_value(instance, key, value):
"""
state, dict_ = instance_state(instance), instance_dict(instance)
- state.get_impl(key).set_committed_value(state, dict_, value)
+ state.manager[key].impl.set_committed_value(state, dict_, value)
def set_attribute(instance, key, value):
"""Set the value of an attribute, firing history events.
@@ -1205,7 +1205,7 @@ def set_attribute(instance, key, value):
"""
state, dict_ = instance_state(instance), instance_dict(instance)
- state.get_impl(key).set(state, dict_, value, None)
+ state.manager[key].impl.set(state, dict_, value, None)
def get_attribute(instance, key):
"""Get the value of an attribute, firing any callables required.
@@ -1218,7 +1218,7 @@ def get_attribute(instance, key):
"""
state, dict_ = instance_state(instance), instance_dict(instance)
- return state.get_impl(key).get(state, dict_)
+ return state.manager[key].impl.get(state, dict_)
def del_attribute(instance, key):
"""Delete the value of an attribute, firing history events.
@@ -1231,5 +1231,5 @@ def del_attribute(instance, key):
"""
state, dict_ = instance_state(instance), instance_dict(instance)
- state.get_impl(key).delete(state, dict_)
+ state.manager[key].impl.delete(state, dict_)
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 20242c97c..cd9f01f38 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -1232,7 +1232,7 @@ class Mapper(object):
A list of values indicating the identifier.
"""
- return self._identity_class, tuple(util.to_list(primary_key))
+ return self._identity_class, tuple(primary_key)
def identity_key_from_instance(self, instance):
"""Return the identity key for the given instance, based on
@@ -1910,7 +1910,7 @@ class Mapper(object):
# refresh whatever has been expired.
if self.eager_defaults and state.unloaded:
state.key = self._identity_key_from_state(state)
- uowtransaction.session.query(self)._get(
+ uowtransaction.session.query(self)._load_on_ident(
state.key, refresh_state=state,
only_load_props=state.unloaded)
@@ -2511,7 +2511,7 @@ def _load_scalar_attributes(state, attribute_names):
statement = mapper._optimized_get_statement(state, attribute_names)
if statement is not None:
result = session.query(mapper).from_statement(statement).\
- _get(None,
+ _load_on_ident(None,
only_load_props=attribute_names,
refresh_state=state)
@@ -2539,7 +2539,7 @@ def _load_scalar_attributes(state, attribute_names):
% state_str(state))
return
- result = session.query(mapper)._get(
+ result = session.query(mapper)._load_on_ident(
identity_key,
refresh_state=state,
only_load_props=attribute_names)
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index 20f71bb51..58c224636 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -612,18 +612,43 @@ class Query(object):
given identifier, or None if not found.
The `ident` argument is a scalar or tuple of primary key column values
- in the order of the table def's primary key columns.
+ in the order of the mapper's "priamry key" setting, which
+ defaults to the list of primary key columns for the
+ mapped :class:`.Table`.
"""
# convert composite types to individual args
if hasattr(ident, '__composite_values__'):
ident = ident.__composite_values__()
-
- key = self._only_mapper_zero(
+
+ ident = util.to_list(ident)
+
+ mapper = self._only_mapper_zero(
"get() can only be used against a single mapped class."
- ).identity_key_from_primary_key(ident)
- return self._get(key, ident)
+ )
+
+ if len(ident) != len(mapper.primary_key):
+ raise sa_exc.InvalidRequestError(
+ "Incorrect number of values in identifier to formulate "
+ "primary key for query.get(); primary key columns are %s" %
+ ','.join("'%s'" % c for c in mapper.primary_key))
+
+ key = mapper.identity_key_from_primary_key(ident)
+
+ if not self._populate_existing and \
+ not mapper.always_refresh and \
+ self._lockmode is None:
+
+ instance = self._get_from_identity(self.session, key, False)
+ if instance is not None:
+ # reject calls for id in identity map but class
+ # mismatch.
+ if not issubclass(instance.__class__, mapper.class_):
+ return None
+ return instance
+
+ return self._load_on_ident(key)
@_generative()
def correlate(self, *args):
@@ -1880,43 +1905,42 @@ class Query(object):
finally:
session.autoflush = autoflush
+ @classmethod
+ def _get_from_identity(cls, session, key, passive):
+ """Look up the given key in the given session's identity map,
+ check the object for expired state if found.
- def _get(self, key=None, ident=None, refresh_state=None, lockmode=None,
- only_load_props=None, passive=None):
- lockmode = lockmode or self._lockmode
-
- mapper = self._mapper_zero()
- if not self._populate_existing and \
- not refresh_state and \
- not mapper.always_refresh and \
- lockmode is None:
- instance = self.session.identity_map.get(key)
- if instance:
- # item present in identity map with a different class
- if not issubclass(instance.__class__, mapper.class_):
+ """
+ instance = session.identity_map.get(key)
+ if instance:
+
+ state = attributes.instance_state(instance)
+
+ # expired - ensure it still exists
+ if state.expired:
+ if passive is attributes.PASSIVE_NO_FETCH:
+ # TODO: no coverage here
+ return attributes.PASSIVE_NO_RESULT
+ try:
+ state()
+ except orm_exc.ObjectDeletedError:
+ session._remove_newly_deleted(state)
return None
-
- state = attributes.instance_state(instance)
-
- # expired - ensure it still exists
- if state.expired:
- if passive is attributes.PASSIVE_NO_FETCH:
- return attributes.PASSIVE_NO_RESULT
- try:
- state()
- except orm_exc.ObjectDeletedError:
- self.session._remove_newly_deleted(state)
- return None
- return instance
- elif passive is attributes.PASSIVE_NO_FETCH:
- return attributes.PASSIVE_NO_RESULT
-
- if ident is None:
- if key is not None:
- ident = key[1]
+ return instance
else:
- ident = util.to_list(ident)
+ return None
+
+ def _load_on_ident(self, key, refresh_state=None, lockmode=None,
+ only_load_props=None):
+ """Load the given identity key from the database."""
+
+ lockmode = lockmode or self._lockmode
+ if key is not None:
+ ident = key[1]
+ else:
+ ident = None
+
if refresh_state is None:
q = self._clone()
q._get_condition()
@@ -1924,11 +1948,7 @@ class Query(object):
q = self._clone()
if ident is not None:
- if len(ident) != len(mapper.primary_key):
- raise sa_exc.InvalidRequestError(
- "Incorrect number of values in identifier to formulate "
- "primary key for query.get(); primary key columns are %s" %
- ','.join("'%s'" % c for c in mapper.primary_key))
+ mapper = self._mapper_zero()
(_get_clause, _get_params) = mapper._get_clause
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index 30a84bf1a..e2c1308b8 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -906,7 +906,7 @@ class Session(object):
self._expire_state(state, attribute_names)
- if self.query(_object_mapper(instance))._get(
+ if self.query(_object_mapper(instance))._load_on_ident(
state.key, refresh_state=state,
lockmode=lockmode,
only_load_props=attribute_names) is None:
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index 1cea5349a..a6711ae26 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -12,12 +12,14 @@ from sqlalchemy import sql, util, log
from sqlalchemy.sql import util as sql_util
from sqlalchemy.sql import visitors, expression, operators
from sqlalchemy.orm import mapper, attributes, interfaces, exc as orm_exc
+from sqlalchemy.orm.mapper import _none_set
from sqlalchemy.orm.interfaces import (
LoaderStrategy, StrategizedOption, MapperOption, PropertyOption,
serialize_path, deserialize_path, StrategizedProperty
)
from sqlalchemy.orm import session as sessionlib
from sqlalchemy.orm import util as mapperutil
+from sqlalchemy.orm.query import Query
import itertools
def _register_attribute(strategy, mapper, useobject,
@@ -282,13 +284,6 @@ class LoadDeferredColumns(object):
# narrow the keys down to just those which have no history
group = [k for k in toload if k in state.unmodified]
- if strategy._should_log_debug():
- strategy.logger.debug(
- "deferred load %s group %s",
- (mapperutil.state_attribute_str(state, self.key),
- group and ','.join(group) or 'None')
- )
-
session = sessionlib._state_session(state)
if session is None:
raise orm_exc.DetachedInstanceError(
@@ -298,8 +293,7 @@ class LoadDeferredColumns(object):
)
query = session.query(localparent)
- ident = state.key[1]
- query._get(None, ident=ident,
+ query._load_on_ident(state.key,
only_load_props=group, refresh_state=state)
return attributes.ATTR_WAS_SET
@@ -588,11 +582,6 @@ class LoadLazyAttribute(object):
):
return attributes.PASSIVE_NO_RESULT
- if strategy._should_log_debug():
- strategy.logger.debug("loading %s",
- mapperutil.state_attribute_str(
- state, self.key))
-
session = sessionlib._state_session(state)
if session is None:
raise orm_exc.DetachedInstanceError(
@@ -600,52 +589,50 @@ class LoadLazyAttribute(object):
"lazy load operation of attribute '%s' cannot proceed" %
(mapperutil.state_str(state), self.key)
)
-
- q = session.query(prop.mapper)._adapt_all_clauses()
-
- # don't autoflush on pending
- # this would be something that's prominent in the
- # docs and such
- if pending:
- q = q.autoflush(False)
-
- if state.load_path:
- q = q._with_current_path(state.load_path + (self.key,))
- # if we have a simple primary key load, use mapper.get()
- # to possibly save a DB round trip
+ # if we have a simple primary key load, check the
+ # identity map without generating a Query at all
if strategy.use_get:
- ident = []
- allnulls = True
if session._flushing:
get_attr = instance_mapper._get_committed_state_attr_by_column
else:
get_attr = instance_mapper._get_state_attr_by_column
-
- # The many-to-one get is intended to be very fast. Note
- # that we don't want to autoflush() if the get() doesn't
- # actually have to hit the DB. It is now not necessary
- # now that we use the pending attribute state.
- for primary_key in prop.mapper.primary_key:
- val = get_attr(
- state,
- state.dict,
- strategy._equated_columns[primary_key],
- passive=passive)
- if val is attributes.PASSIVE_NO_RESULT:
- return val
- allnulls = allnulls and val is None
- ident.append(val)
+
+ ident = [
+ get_attr(
+ state,
+ state.dict,
+ strategy._equated_columns[pk],
+ passive=passive)
+ for pk in prop.mapper.primary_key
+ ]
+ if attributes.PASSIVE_NO_RESULT in ident:
+ return attributes.PASSIVE_NO_RESULT
- if allnulls:
+ if _none_set.issuperset(ident):
return None
- if state.load_options:
- q = q._conditional_options(*state.load_options)
-
key = prop.mapper.identity_key_from_primary_key(ident)
- return q._get(key, ident, passive=passive)
+ instance = Query._get_from_identity(session, key, passive)
+ if instance is not None:
+ return instance
+ elif passive is attributes.PASSIVE_NO_FETCH:
+ return attributes.PASSIVE_NO_RESULT
+
+ q = session.query(prop.mapper)._adapt_all_clauses()
+
+ # don't autoflush on pending
+ if pending:
+ q = q.autoflush(False)
+ if state.load_path:
+ q = q._with_current_path(state.load_path + (self.key,))
+
+ if state.load_options:
+ q = q._conditional_options(*state.load_options)
+
+ if strategy.use_get:
+ return q._load_on_ident(key)
if prop.order_by:
q = q.order_by(*util.to_list(prop.order_by))
@@ -658,9 +645,6 @@ class LoadLazyAttribute(object):
not isinstance(rev.strategy, LazyLoader):
q = q.options(EagerLazyOption((rev.key,), lazy='select'))
- if state.load_options:
- q = q._conditional_options(*state.load_options)
-
lazy_clause = strategy.lazy_clause(state)
if pending: