summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/sqlalchemy/orm/attributes.py57
-rw-r--r--lib/sqlalchemy/orm/identity.py11
-rw-r--r--lib/sqlalchemy/orm/mapper.py4
-rw-r--r--lib/sqlalchemy/orm/query.py13
-rw-r--r--lib/sqlalchemy/orm/state.py12
-rw-r--r--lib/sqlalchemy/orm/strategies.py23
6 files changed, 68 insertions, 52 deletions
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py
index f6947dbc1..3ca7d8311 100644
--- a/lib/sqlalchemy/orm/attributes.py
+++ b/lib/sqlalchemy/orm/attributes.py
@@ -28,7 +28,7 @@ _entity_info = None
identity_equal = None
state = None
-PASSIVE_NORESULT = util.symbol('PASSIVE_NORESULT')
+PASSIVE_NO_RESULT = util.symbol('PASSIVE_NO_RESULT')
ATTR_WAS_SET = util.symbol('ATTR_WAS_SET')
NO_VALUE = util.symbol('NO_VALUE')
NEVER_SET = util.symbol('NEVER_SET')
@@ -43,7 +43,7 @@ PASSIVE_NO_INITIALIZE = True #util.symbol('PASSIVE_NO_INITIALIZE')
# don't fire off any callables, but if no callables present
# then initialize to an empty value/collection
# this is used by backrefs.
-PASSIVE_NO_CALLABLES = util.symbol('PASSIVE_NO_CALLABLES')
+PASSIVE_NO_FETCH = util.symbol('PASSIVE_NO_FETCH')
# fire callables/initialize as needed
PASSIVE_OFF = False #util.symbol('PASSIVE_OFF')
@@ -368,14 +368,16 @@ class AttributeImpl(object):
# if no history, check for lazy callables, etc.
if state.committed_state.get(self.key, NEVER_SET) is NEVER_SET:
if passive is PASSIVE_NO_INITIALIZE:
- return PASSIVE_NORESULT
+ return PASSIVE_NO_RESULT
callable_ = self._get_callable(state)
if callable_ is not None:
- if passive is not PASSIVE_OFF:
- return PASSIVE_NORESULT
- value = callable_()
- if value is not ATTR_WAS_SET:
+ #if passive is not PASSIVE_OFF:
+ # return PASSIVE_NO_RESULT
+ value = callable_(passive=passive)
+ if value is PASSIVE_NO_RESULT:
+ return value
+ elif value is not ATTR_WAS_SET:
return self.set_committed_value(state, dict_, value)
else:
if self.key not in dict_:
@@ -504,7 +506,7 @@ class MutableScalarAttributeImpl(ScalarAttributeImpl):
def get(self, state, dict_, passive=PASSIVE_OFF):
if self.key not in state.mutable_dict:
ret = ScalarAttributeImpl.get(self, state, dict_, passive=passive)
- if ret is not PASSIVE_NORESULT:
+ if ret is not PASSIVE_NO_RESULT:
state.mutable_dict[self.key] = ret
return ret
else:
@@ -557,7 +559,7 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl):
return History.from_attribute(self, state, dict_[self.key])
else:
current = self.get(state, dict_, passive=passive)
- if current is PASSIVE_NORESULT:
+ if current is PASSIVE_NO_RESULT:
return HISTORY_BLANK
else:
return History.from_attribute(self, state, current)
@@ -576,16 +578,7 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl):
if self.active_history:
old = self.get(state, dict_)
else:
- # this would be the "laziest" approach,
- # however it breaks currently expected backref
- # behavior
- #old = dict_.get(self.key, None)
- # instead, use the "passive" setting, which
- # is only going to be PASSIVE_NOCALLABLES if it
- # came from a backref
- old = self.get(state, dict_, passive=passive)
- if old is PASSIVE_NORESULT:
- old = None
+ old = self.get(state, dict_, passive=PASSIVE_NO_FETCH)
value = self.fire_replace_event(state, dict_, value, old, initiator)
dict_[self.key] = value
@@ -603,7 +596,7 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl):
state.modified_event(dict_, self, False, previous)
if self.trackparent:
- if previous is not value and previous is not None:
+ if previous is not value and previous not in (None, PASSIVE_NO_RESULT):
self.sethasparent(instance_state(previous), False)
for ext in self.extensions:
@@ -650,7 +643,7 @@ class CollectionAttributeImpl(AttributeImpl):
def get_history(self, state, dict_, passive=PASSIVE_OFF):
current = self.get(state, dict_, passive=passive)
- if current is PASSIVE_NORESULT:
+ if current is PASSIVE_NO_RESULT:
return HISTORY_BLANK
else:
return History.from_attribute(self, state, current)
@@ -705,7 +698,7 @@ class CollectionAttributeImpl(AttributeImpl):
return
collection = self.get_collection(state, dict_, passive=passive)
- if collection is PASSIVE_NORESULT:
+ if collection is PASSIVE_NO_RESULT:
value = self.fire_append_event(state, dict_, value, initiator)
state.get_pending(self.key).append(value)
else:
@@ -716,7 +709,7 @@ class CollectionAttributeImpl(AttributeImpl):
return
collection = self.get_collection(state, state.dict, passive=passive)
- if collection is PASSIVE_NORESULT:
+ if collection is PASSIVE_NO_RESULT:
self.fire_remove_event(state, dict_, value, initiator)
state.get_pending(self.key).remove(value)
else:
@@ -810,7 +803,7 @@ class CollectionAttributeImpl(AttributeImpl):
"""
if user_data is None:
user_data = self.get(state, dict_, passive=passive)
- if user_data is PASSIVE_NORESULT:
+ if user_data is PASSIVE_NO_RESULT:
return user_data
return getattr(user_data, '_sa_adapter')
@@ -832,29 +825,29 @@ class GenericBackrefExtension(interfaces.AttributeExtension):
def set(self, state, child, oldchild, initiator):
if oldchild is child:
return child
- if oldchild is not None:
+ if oldchild not in (None, PASSIVE_NO_RESULT):
# With lazy=None, there's no guarantee that the full collection is
# present when updating via a backref.
old_state, old_dict = instance_state(oldchild), instance_dict(oldchild)
impl = old_state.get_impl(self.key)
try:
- impl.remove(old_state, old_dict, state.obj(), initiator, passive=PASSIVE_NO_CALLABLES)
+ impl.remove(old_state, old_dict, state.obj(), initiator, passive=PASSIVE_NO_FETCH)
except (ValueError, KeyError, IndexError):
pass
if child is not None:
new_state, new_dict = instance_state(child), instance_dict(child)
- new_state.get_impl(self.key).append(new_state, new_dict, state.obj(), initiator, passive=PASSIVE_NO_CALLABLES)
+ new_state.get_impl(self.key).append(new_state, new_dict, state.obj(), initiator, passive=PASSIVE_NO_FETCH)
return child
def append(self, state, child, initiator):
child_state, child_dict = instance_state(child), instance_dict(child)
- child_state.get_impl(self.key).append(child_state, child_dict, state.obj(), initiator, passive=PASSIVE_NO_CALLABLES)
+ child_state.get_impl(self.key).append(child_state, child_dict, state.obj(), initiator, passive=PASSIVE_NO_FETCH)
return child
def remove(self, state, child, initiator):
if child is not None:
child_state, child_dict = instance_state(child), instance_dict(child)
- child_state.get_impl(self.key).remove(child_state, child_dict, state.obj(), initiator, passive=PASSIVE_NO_CALLABLES)
+ child_state.get_impl(self.key).remove(child_state, child_dict, state.obj(), initiator, passive=PASSIVE_NO_FETCH)
class Events(object):
@@ -1248,9 +1241,9 @@ class History(tuple):
def as_state(self):
return History(
- [c is not None and instance_state(c) or None for c in self.added],
- [c is not None and instance_state(c) or None for c in self.unchanged],
- [c is not None and instance_state(c) or None for c in self.deleted],
+ [c not in (None, PASSIVE_NO_RESULT) and instance_state(c) or None for c in self.added],
+ [c not in (None, PASSIVE_NO_RESULT) and instance_state(c) or None for c in self.unchanged],
+ [c not in (None, PASSIVE_NO_RESULT) and instance_state(c) or None for c in self.deleted],
)
@classmethod
diff --git a/lib/sqlalchemy/orm/identity.py b/lib/sqlalchemy/orm/identity.py
index b7d4234f4..889b65ba7 100644
--- a/lib/sqlalchemy/orm/identity.py
+++ b/lib/sqlalchemy/orm/identity.py
@@ -141,10 +141,15 @@ class WeakInstanceDict(IdentityMap):
self._manage_removed_state(state)
def get(self, key, default=None):
- try:
- return self[key]
- except KeyError:
+ state = dict.get(self, key, default)
+ if state is default:
+ return default
+ o = state.obj()
+ if o is None:
+ o = state._is_really_none()
+ if o is None:
return default
+ return o
# Py2K
def items(self):
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index c2c57825e..177799602 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -1124,12 +1124,12 @@ class Mapper(object):
if leftcol.table not in tables:
leftval = self._get_committed_state_attr_by_column(state, leftcol, passive=True)
- if leftval is attributes.PASSIVE_NORESULT:
+ if leftval is attributes.PASSIVE_NO_RESULT:
raise ColumnsNotAvailable()
binary.left = sql.bindparam(None, leftval, type_=binary.right.type)
elif rightcol.table not in tables:
rightval = self._get_committed_state_attr_by_column(state, rightcol, passive=True)
- if rightval is attributes.PASSIVE_NORESULT:
+ if rightval is attributes.PASSIVE_NO_RESULT:
raise ColumnsNotAvailable()
binary.right = sql.bindparam(None, rightval, type_=binary.right.type)
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index 21137bc28..f09b594c0 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -1447,21 +1447,24 @@ class Query(object):
break
iterate_instances = util.deprecated()(instances)
- def _get(self, key=None, ident=None, refresh_state=None, lockmode=None, only_load_props=None):
+ def _get(self, key=None, ident=None, refresh_state=None, lockmode=None, only_load_props=None, passive=None):
lockmode = lockmode or self._lockmode
if not self._populate_existing and not refresh_state and not self._mapper_zero().always_refresh and lockmode is None:
- try:
- instance = self.session.identity_map[key]
+ instance = self.session.identity_map.get(key)
+ if instance:
state = attributes.instance_state(instance)
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
- except KeyError:
- pass
+ elif passive is attributes.PASSIVE_NO_FETCH:
+ return attributes.PASSIVE_NO_RESULT
if ident is None:
if key is not None:
diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py
index 4d9fa5ade..f09c59763 100644
--- a/lib/sqlalchemy/orm/state.py
+++ b/lib/sqlalchemy/orm/state.py
@@ -1,7 +1,7 @@
from sqlalchemy.util import EMPTY_SET
import weakref
from sqlalchemy import util
-from sqlalchemy.orm.attributes import PASSIVE_NORESULT, PASSIVE_OFF, NEVER_SET, NO_VALUE, manager_of_class, ATTR_WAS_SET
+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
from sqlalchemy.orm import interfaces
@@ -108,13 +108,13 @@ class InstanceState(object):
attribute.
returns None if passive is not PASSIVE_OFF and the getter returns
- PASSIVE_NORESULT.
+ PASSIVE_NO_RESULT.
"""
impl = self.get_impl(key)
dict_ = self.dict
x = impl.get(self, dict_, passive=passive)
- if x is PASSIVE_NORESULT:
+ if x is PASSIVE_NO_RESULT:
return None
elif hasattr(impl, 'get_collection'):
return impl.get_collection(self, dict_, x, passive=passive)
@@ -176,12 +176,16 @@ class InstanceState(object):
self.dict.pop(key, None)
self.callables[key] = callable_
- def __call__(self):
+ def __call__(self, **kw):
"""__call__ allows the InstanceState to act as a deferred
callable for loading expired attributes, which is also
serializable (picklable).
"""
+
+ if kw.get('passive') is attributes.PASSIVE_NO_FETCH:
+ return attributes.PASSIVE_NO_RESULT
+
unmodified = self.unmodified
class_manager = self.manager
class_manager.deferred_scalar_loader(self, [
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index e19e8fb31..c0609dba3 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -261,10 +261,12 @@ class LoadDeferredColumns(object):
def __init__(self, state, key):
self.state, self.key = state, key
- def __call__(self):
+ def __call__(self, **kw):
+ if kw.get('passive') is attributes.PASSIVE_NO_FETCH:
+ return attributes.PASSIVE_NO_RESULT
+
state = self.state
-
localparent = mapper._state_mapper(state)
prop = localparent.get_property(self.key)
@@ -536,12 +538,14 @@ class LoadLazyAttribute(object):
def __setstate__(self, state):
self.state, self.key = state
- def __call__(self):
+ def __call__(self, **kw):
state = self.state
-
instance_mapper = mapper._state_mapper(state)
prop = instance_mapper.get_property(self.key)
strategy = prop._get_strategy(LazyLoader)
+
+ if kw.get('passive') is attributes.PASSIVE_NO_FETCH and not strategy.use_get:
+ return attributes.PASSIVE_NO_RESULT
if strategy._should_log_debug:
strategy.logger.debug("loading %s" % mapperutil.state_attribute_str(state, self.key))
@@ -565,14 +569,21 @@ class LoadLazyAttribute(object):
ident = []
allnulls = True
for primary_key in prop.mapper.primary_key:
- val = instance_mapper._get_committed_state_attr_by_column(state, strategy._equated_columns[primary_key])
+ val = instance_mapper._get_committed_state_attr_by_column(state, strategy._equated_columns[primary_key], **kw)
+ if val is attributes.PASSIVE_NO_RESULT:
+ return val
allnulls = allnulls and val is None
ident.append(val)
+
if allnulls:
return None
+
if state.load_options:
q = q._conditional_options(*state.load_options)
- return q.get(ident)
+
+ key = prop.mapper.identity_key_from_primary_key(ident)
+ return q._get(key, ident, **kw)
+
if prop.order_by:
q = q.order_by(*util.to_list(prop.order_by))