Update SQLAlchemy
This commit is contained in:
+287
-278
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
# orm/attributes.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -29,7 +29,7 @@ NO_VALUE = util.symbol('NO_VALUE')
|
||||
NEVER_SET = util.symbol('NEVER_SET')
|
||||
|
||||
PASSIVE_RETURN_NEVER_SET = util.symbol('PASSIVE_RETURN_NEVER_SET',
|
||||
"""Symbol indicating that loader callables can be
|
||||
"""Symbol indicating that loader callables can be
|
||||
fired off, but if no callable is applicable and no value is
|
||||
present, the attribute should remain non-initialized.
|
||||
NEVER_SET is returned in this case.
|
||||
@@ -37,14 +37,14 @@ NEVER_SET is returned in this case.
|
||||
|
||||
PASSIVE_NO_INITIALIZE = util.symbol('PASSIVE_NO_INITIALIZE',
|
||||
"""Symbol indicating that loader callables should
|
||||
not be fired off, and a non-initialized attribute
|
||||
not be fired off, and a non-initialized attribute
|
||||
should remain that way.
|
||||
""")
|
||||
|
||||
PASSIVE_NO_FETCH = util.symbol('PASSIVE_NO_FETCH',
|
||||
"""Symbol indicating that loader callables should not emit SQL,
|
||||
"""Symbol indicating that loader callables should not emit SQL,
|
||||
but a value can be fetched from the current session.
|
||||
|
||||
|
||||
Non-initialized attributes should be initialized to an empty value.
|
||||
|
||||
""")
|
||||
@@ -53,9 +53,9 @@ PASSIVE_NO_FETCH_RELATED = util.symbol('PASSIVE_NO_FETCH_RELATED',
|
||||
"""Symbol indicating that loader callables should not emit SQL for
|
||||
loading a related object, but can refresh the attributes of the local
|
||||
instance in order to locate a related object in the current session.
|
||||
|
||||
|
||||
Non-initialized attributes should be initialized to an empty value.
|
||||
|
||||
|
||||
The unit of work uses this mode to check if history is present
|
||||
on many-to-one attributes with minimal SQL emitted.
|
||||
|
||||
@@ -81,7 +81,7 @@ PASSIVE_OFF = util.symbol('PASSIVE_OFF',
|
||||
class QueryableAttribute(interfaces.PropComparator):
|
||||
"""Base class for class-bound attributes. """
|
||||
|
||||
def __init__(self, class_, key, impl=None,
|
||||
def __init__(self, class_, key, impl=None,
|
||||
comparator=None, parententity=None):
|
||||
self.class_ = class_
|
||||
self.key = key
|
||||
@@ -92,7 +92,7 @@ class QueryableAttribute(interfaces.PropComparator):
|
||||
manager = manager_of_class(class_)
|
||||
# manager is None in the case of AliasedClass
|
||||
if manager:
|
||||
# propagate existing event listeners from
|
||||
# propagate existing event listeners from
|
||||
# immediate superclass
|
||||
for base in manager._bases:
|
||||
if key in base:
|
||||
@@ -134,8 +134,8 @@ class QueryableAttribute(interfaces.PropComparator):
|
||||
except AttributeError:
|
||||
raise AttributeError(
|
||||
'Neither %r object nor %r object has an attribute %r' % (
|
||||
type(self).__name__,
|
||||
type(self.comparator).__name__,
|
||||
type(self).__name__,
|
||||
type(self.comparator).__name__,
|
||||
key)
|
||||
)
|
||||
|
||||
@@ -151,7 +151,7 @@ class InstrumentedAttribute(QueryableAttribute):
|
||||
"""Class bound instrumented attribute which adds descriptor methods."""
|
||||
|
||||
def __set__(self, instance, value):
|
||||
self.impl.set(instance_state(instance),
|
||||
self.impl.set(instance_state(instance),
|
||||
instance_dict(instance), value, None)
|
||||
|
||||
def __delete__(self, instance):
|
||||
@@ -179,12 +179,12 @@ def create_proxied_attribute(descriptor):
|
||||
|
||||
class Proxy(QueryableAttribute):
|
||||
"""Presents the :class:`.QueryableAttribute` interface as a
|
||||
proxy on top of a Python descriptor / :class:`.PropComparator`
|
||||
proxy on top of a Python descriptor / :class:`.PropComparator`
|
||||
combination.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, class_, key, descriptor, comparator,
|
||||
def __init__(self, class_, key, descriptor, comparator,
|
||||
adapter=None, doc=None):
|
||||
self.class_ = class_
|
||||
self.key = key
|
||||
@@ -233,8 +233,8 @@ def create_proxied_attribute(descriptor):
|
||||
except AttributeError:
|
||||
raise AttributeError(
|
||||
'Neither %r object nor %r object has an attribute %r' % (
|
||||
type(descriptor).__name__,
|
||||
type(self.comparator).__name__,
|
||||
type(descriptor).__name__,
|
||||
type(self.comparator).__name__,
|
||||
attribute)
|
||||
)
|
||||
|
||||
@@ -250,7 +250,7 @@ class AttributeImpl(object):
|
||||
|
||||
def __init__(self, class_, key,
|
||||
callable_, dispatch, trackparent=False, extension=None,
|
||||
compare_function=None, active_history=False,
|
||||
compare_function=None, active_history=False,
|
||||
parent_token=None, expire_missing=True,
|
||||
**kwargs):
|
||||
"""Construct an AttributeImpl.
|
||||
@@ -287,12 +287,12 @@ class AttributeImpl(object):
|
||||
parent_token
|
||||
Usually references the MapperProperty, used as a key for
|
||||
the hasparent() function to identify an "owning" attribute.
|
||||
Allows multiple AttributeImpls to all match a single
|
||||
Allows multiple AttributeImpls to all match a single
|
||||
owner attribute.
|
||||
|
||||
expire_missing
|
||||
if False, don't add an "expiry" callable to this attribute
|
||||
during state.expire_attributes(None), if no value is present
|
||||
during state.expire_attributes(None), if no value is present
|
||||
for this key.
|
||||
|
||||
"""
|
||||
@@ -331,7 +331,7 @@ class AttributeImpl(object):
|
||||
|
||||
|
||||
def hasparent(self, state, optimistic=False):
|
||||
"""Return the boolean value of a `hasparent` flag attached to
|
||||
"""Return the boolean value of a `hasparent` flag attached to
|
||||
the given state.
|
||||
|
||||
The `optimistic` flag determines what the default return value
|
||||
@@ -375,8 +375,8 @@ class AttributeImpl(object):
|
||||
"state %s along attribute '%s', "
|
||||
"but the parent record "
|
||||
"has gone stale, can't be sure this "
|
||||
"is the most recent parent." %
|
||||
(mapperutil.state_str(state),
|
||||
"is the most recent parent." %
|
||||
(mapperutil.state_str(state),
|
||||
mapperutil.state_str(parent_state),
|
||||
self.key))
|
||||
|
||||
@@ -406,18 +406,18 @@ class AttributeImpl(object):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_all_pending(self, state, dict_):
|
||||
"""Return a list of tuples of (state, obj)
|
||||
for all objects in this attribute's current state
|
||||
"""Return a list of tuples of (state, obj)
|
||||
for all objects in this attribute's current state
|
||||
+ history.
|
||||
|
||||
Only applies to object-based attributes.
|
||||
|
||||
This is an inlining of existing functionality
|
||||
which roughly correponds to:
|
||||
which roughly corresponds to:
|
||||
|
||||
get_state_history(
|
||||
state,
|
||||
key,
|
||||
state,
|
||||
key,
|
||||
passive=PASSIVE_NO_INITIALIZE).sum()
|
||||
|
||||
"""
|
||||
@@ -478,14 +478,14 @@ class AttributeImpl(object):
|
||||
self.set(state, dict_, value, initiator, passive=passive)
|
||||
|
||||
def remove(self, state, dict_, value, initiator, passive=PASSIVE_OFF):
|
||||
self.set(state, dict_, None, initiator,
|
||||
self.set(state, dict_, None, initiator,
|
||||
passive=passive, check_old=value)
|
||||
|
||||
def pop(self, state, dict_, value, initiator, passive=PASSIVE_OFF):
|
||||
self.set(state, dict_, None, initiator,
|
||||
self.set(state, dict_, None, initiator,
|
||||
passive=passive, check_old=value, pop=True)
|
||||
|
||||
def set(self, state, dict_, value, initiator,
|
||||
def set(self, state, dict_, value, initiator,
|
||||
passive=PASSIVE_OFF, check_old=None, pop=False):
|
||||
raise NotImplementedError()
|
||||
|
||||
@@ -532,7 +532,7 @@ class ScalarAttributeImpl(AttributeImpl):
|
||||
return History.from_scalar_attribute(
|
||||
self, state, dict_.get(self.key, NO_VALUE))
|
||||
|
||||
def set(self, state, dict_, value, initiator,
|
||||
def set(self, state, dict_, value, initiator,
|
||||
passive=PASSIVE_OFF, check_old=None, pop=False):
|
||||
if initiator and initiator.parent_token is self.parent_token:
|
||||
return
|
||||
@@ -543,7 +543,7 @@ class ScalarAttributeImpl(AttributeImpl):
|
||||
old = dict_.get(self.key, NO_VALUE)
|
||||
|
||||
if self.dispatch.set:
|
||||
value = self.fire_replace_event(state, dict_,
|
||||
value = self.fire_replace_event(state, dict_,
|
||||
value, old, initiator)
|
||||
state.modified_event(dict_, self, old)
|
||||
dict_[self.key] = value
|
||||
@@ -575,10 +575,10 @@ class MutableScalarAttributeImpl(ScalarAttributeImpl):
|
||||
class_manager, copy_function=None,
|
||||
compare_function=None, **kwargs):
|
||||
super(ScalarAttributeImpl, self).__init__(
|
||||
class_,
|
||||
key,
|
||||
class_,
|
||||
key,
|
||||
callable_, dispatch,
|
||||
compare_function=compare_function,
|
||||
compare_function=compare_function,
|
||||
**kwargs)
|
||||
class_manager.mutable_attributes.add(key)
|
||||
if copy_function is None:
|
||||
@@ -611,15 +611,15 @@ class MutableScalarAttributeImpl(ScalarAttributeImpl):
|
||||
ScalarAttributeImpl.delete(self, state, dict_)
|
||||
state.mutable_dict.pop(self.key)
|
||||
|
||||
def set(self, state, dict_, value, initiator,
|
||||
def set(self, state, dict_, value, initiator,
|
||||
passive=PASSIVE_OFF, check_old=None, pop=False):
|
||||
ScalarAttributeImpl.set(self, state, dict_, value,
|
||||
ScalarAttributeImpl.set(self, state, dict_, value,
|
||||
initiator, passive, check_old=check_old, pop=pop)
|
||||
state.mutable_dict[self.key] = value
|
||||
|
||||
|
||||
class ScalarObjectAttributeImpl(ScalarAttributeImpl):
|
||||
"""represents a scalar-holding InstrumentedAttribute,
|
||||
"""represents a scalar-holding InstrumentedAttribute,
|
||||
where the target object is also instrumented.
|
||||
|
||||
Adds events to delete/set operations.
|
||||
@@ -653,7 +653,7 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl):
|
||||
if current is not None:
|
||||
ret = [(instance_state(current), current)]
|
||||
else:
|
||||
ret = []
|
||||
ret = [(None, None)]
|
||||
|
||||
if self.key in state.committed_state:
|
||||
original = state.committed_state[self.key]
|
||||
@@ -665,7 +665,7 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl):
|
||||
else:
|
||||
return []
|
||||
|
||||
def set(self, state, dict_, value, initiator,
|
||||
def set(self, state, dict_, value, initiator,
|
||||
passive=PASSIVE_OFF, check_old=None, pop=False):
|
||||
"""Set a value on the given InstanceState.
|
||||
|
||||
@@ -686,7 +686,7 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl):
|
||||
old is not PASSIVE_NO_RESULT and \
|
||||
check_old is not old:
|
||||
if pop:
|
||||
return
|
||||
return
|
||||
else:
|
||||
raise ValueError(
|
||||
"Object %s not associated with %s on attribute '%s'" % (
|
||||
@@ -744,12 +744,12 @@ class CollectionAttributeImpl(AttributeImpl):
|
||||
typecallable=None, trackparent=False, extension=None,
|
||||
copy_function=None, compare_function=None, **kwargs):
|
||||
super(CollectionAttributeImpl, self).__init__(
|
||||
class_,
|
||||
key,
|
||||
class_,
|
||||
key,
|
||||
callable_, dispatch,
|
||||
trackparent=trackparent,
|
||||
extension=extension,
|
||||
compare_function=compare_function,
|
||||
compare_function=compare_function,
|
||||
**kwargs)
|
||||
|
||||
if copy_function is None:
|
||||
@@ -777,11 +777,11 @@ class CollectionAttributeImpl(AttributeImpl):
|
||||
if self.key in state.committed_state:
|
||||
original = state.committed_state[self.key]
|
||||
if original is not NO_VALUE:
|
||||
current_states = [((c is not None) and
|
||||
instance_state(c) or None, c)
|
||||
current_states = [((c is not None) and
|
||||
instance_state(c) or None, c)
|
||||
for c in current]
|
||||
original_states = [((c is not None) and
|
||||
instance_state(c) or None, c)
|
||||
original_states = [((c is not None) and
|
||||
instance_state(c) or None, c)
|
||||
for c in original]
|
||||
|
||||
current_set = dict(current_states)
|
||||
@@ -869,13 +869,13 @@ class CollectionAttributeImpl(AttributeImpl):
|
||||
def pop(self, state, dict_, value, initiator, passive=PASSIVE_OFF):
|
||||
try:
|
||||
# TODO: better solution here would be to add
|
||||
# a "popper" role to collections.py to complement
|
||||
# a "popper" role to collections.py to complement
|
||||
# "remover".
|
||||
self.remove(state, dict_, value, initiator, passive=passive)
|
||||
except (ValueError, KeyError, IndexError):
|
||||
pass
|
||||
|
||||
def set(self, state, dict_, value, initiator,
|
||||
def set(self, state, dict_, value, initiator,
|
||||
passive=PASSIVE_OFF, pop=False):
|
||||
"""Set a value on the given object.
|
||||
|
||||
@@ -954,7 +954,7 @@ class CollectionAttributeImpl(AttributeImpl):
|
||||
|
||||
return user_data
|
||||
|
||||
def get_collection(self, state, dict_,
|
||||
def get_collection(self, state, dict_,
|
||||
user_data=None, passive=PASSIVE_OFF):
|
||||
"""Retrieve the CollectionAdapter associated with the given state.
|
||||
|
||||
@@ -973,6 +973,14 @@ def backref_listeners(attribute, key, uselist):
|
||||
|
||||
# use easily recognizable names for stack traces
|
||||
|
||||
parent_token = attribute.impl.parent_token
|
||||
|
||||
def _acceptable_key_err(child_state, initiator):
|
||||
raise ValueError(
|
||||
"Object %s not associated with attribute of "
|
||||
"type %s" % (mapperutil.state_str(child_state),
|
||||
manager_of_class(initiator.class_)[initiator.key]))
|
||||
|
||||
def emit_backref_from_scalar_set_event(state, child, oldchild, initiator):
|
||||
if oldchild is child:
|
||||
return child
|
||||
@@ -983,61 +991,73 @@ def backref_listeners(attribute, key, uselist):
|
||||
old_state, old_dict = instance_state(oldchild),\
|
||||
instance_dict(oldchild)
|
||||
impl = old_state.manager[key].impl
|
||||
impl.pop(old_state,
|
||||
old_dict,
|
||||
state.obj(),
|
||||
impl.pop(old_state,
|
||||
old_dict,
|
||||
state.obj(),
|
||||
initiator, passive=PASSIVE_NO_FETCH)
|
||||
|
||||
if child is not None:
|
||||
child_state, child_dict = instance_state(child),\
|
||||
instance_dict(child)
|
||||
child_state.manager[key].impl.append(
|
||||
child_state,
|
||||
child_dict,
|
||||
state.obj(),
|
||||
initiator,
|
||||
passive=PASSIVE_NO_FETCH)
|
||||
child_impl = child_state.manager[key].impl
|
||||
if initiator.parent_token is not parent_token and \
|
||||
initiator.parent_token is not child_impl.parent_token:
|
||||
_acceptable_key_err(state, initiator)
|
||||
child_impl.append(
|
||||
child_state,
|
||||
child_dict,
|
||||
state.obj(),
|
||||
initiator,
|
||||
passive=PASSIVE_NO_FETCH)
|
||||
return child
|
||||
|
||||
def emit_backref_from_collection_append_event(state, child, initiator):
|
||||
child_state, child_dict = instance_state(child), \
|
||||
instance_dict(child)
|
||||
child_state.manager[key].impl.append(
|
||||
child_state,
|
||||
child_dict,
|
||||
state.obj(),
|
||||
initiator,
|
||||
passive=PASSIVE_NO_FETCH)
|
||||
child_impl = child_state.manager[key].impl
|
||||
if initiator.parent_token is not parent_token and \
|
||||
initiator.parent_token is not child_impl.parent_token:
|
||||
_acceptable_key_err(state, initiator)
|
||||
child_impl.append(
|
||||
child_state,
|
||||
child_dict,
|
||||
state.obj(),
|
||||
initiator,
|
||||
passive=PASSIVE_NO_FETCH)
|
||||
return child
|
||||
|
||||
def emit_backref_from_collection_remove_event(state, child, initiator):
|
||||
if child is not None:
|
||||
child_state, child_dict = instance_state(child),\
|
||||
instance_dict(child)
|
||||
child_state.manager[key].impl.pop(
|
||||
child_state,
|
||||
child_dict,
|
||||
state.obj(),
|
||||
initiator,
|
||||
passive=PASSIVE_NO_FETCH)
|
||||
child_impl = child_state.manager[key].impl
|
||||
# can't think of a path that would produce an initiator
|
||||
# mismatch here, as it would require an existing collection
|
||||
# mismatch.
|
||||
child_impl.pop(
|
||||
child_state,
|
||||
child_dict,
|
||||
state.obj(),
|
||||
initiator,
|
||||
passive=PASSIVE_NO_FETCH)
|
||||
|
||||
if uselist:
|
||||
event.listen(attribute, "append",
|
||||
emit_backref_from_collection_append_event,
|
||||
event.listen(attribute, "append",
|
||||
emit_backref_from_collection_append_event,
|
||||
retval=True, raw=True)
|
||||
else:
|
||||
event.listen(attribute, "set",
|
||||
emit_backref_from_scalar_set_event,
|
||||
event.listen(attribute, "set",
|
||||
emit_backref_from_scalar_set_event,
|
||||
retval=True, raw=True)
|
||||
# TODO: need coverage in test/orm/ of remove event
|
||||
event.listen(attribute, "remove",
|
||||
emit_backref_from_collection_remove_event,
|
||||
event.listen(attribute, "remove",
|
||||
emit_backref_from_collection_remove_event,
|
||||
retval=True, raw=True)
|
||||
|
||||
_NO_HISTORY = util.symbol('NO_HISTORY')
|
||||
_NO_STATE_SYMBOLS = frozenset([
|
||||
id(PASSIVE_NO_RESULT),
|
||||
id(NO_VALUE),
|
||||
id(PASSIVE_NO_RESULT),
|
||||
id(NO_VALUE),
|
||||
id(NEVER_SET)])
|
||||
class History(tuple):
|
||||
"""A 3-tuple of added, unchanged and deleted values,
|
||||
@@ -1078,7 +1098,7 @@ class History(tuple):
|
||||
return not bool(
|
||||
(self.added or self.deleted)
|
||||
or self.unchanged and self.unchanged != [None]
|
||||
)
|
||||
)
|
||||
|
||||
def sum(self):
|
||||
"""Return a collection of added + unchanged + deleted."""
|
||||
@@ -1126,11 +1146,11 @@ class History(tuple):
|
||||
return cls((), (), ())
|
||||
else:
|
||||
return cls((), [current], ())
|
||||
# dont let ClauseElement expressions here trip things up
|
||||
# don't let ClauseElement expressions here trip things up
|
||||
elif attribute.is_equal(current, original) is True:
|
||||
return cls((), [current], ())
|
||||
else:
|
||||
# current convention on native scalars is to not
|
||||
# current convention on native scalars is to not
|
||||
# include information
|
||||
# about missing previous value in "deleted", but
|
||||
# we do include None, which helps in some primary
|
||||
@@ -1156,11 +1176,11 @@ class History(tuple):
|
||||
elif current is original:
|
||||
return cls((), [current], ())
|
||||
else:
|
||||
# current convention on related objects is to not
|
||||
# current convention on related objects is to not
|
||||
# include information
|
||||
# about missing previous value in "deleted", and
|
||||
# to also not include None - the dependency.py rules
|
||||
# ignore the None in any case.
|
||||
# ignore the None in any case.
|
||||
if id(original) in _NO_STATE_SYMBOLS or original is None:
|
||||
deleted = ()
|
||||
else:
|
||||
@@ -1181,11 +1201,11 @@ class History(tuple):
|
||||
return cls((), list(current), ())
|
||||
else:
|
||||
|
||||
current_states = [((c is not None) and instance_state(c) or None, c)
|
||||
for c in current
|
||||
current_states = [((c is not None) and instance_state(c) or None, c)
|
||||
for c in current
|
||||
]
|
||||
original_states = [((c is not None) and instance_state(c) or None, c)
|
||||
for c in original
|
||||
original_states = [((c is not None) and instance_state(c) or None, c)
|
||||
for c in original
|
||||
]
|
||||
|
||||
current_set = dict(current_states)
|
||||
@@ -1200,7 +1220,7 @@ class History(tuple):
|
||||
HISTORY_BLANK = History(None, None, None)
|
||||
|
||||
def get_history(obj, key, passive=PASSIVE_OFF):
|
||||
"""Return a :class:`.History` record for the given object
|
||||
"""Return a :class:`.History` record for the given object
|
||||
and attribute key.
|
||||
|
||||
:param obj: an object whose class is instrumented by the
|
||||
@@ -1239,14 +1259,14 @@ def register_attribute(class_, key, **kw):
|
||||
comparator = kw.pop('comparator', None)
|
||||
parententity = kw.pop('parententity', None)
|
||||
doc = kw.pop('doc', None)
|
||||
desc = register_descriptor(class_, key,
|
||||
desc = register_descriptor(class_, key,
|
||||
comparator, parententity, doc=doc)
|
||||
register_attribute_impl(class_, key, **kw)
|
||||
return desc
|
||||
|
||||
def register_attribute_impl(class_, key,
|
||||
uselist=False, callable_=None,
|
||||
useobject=False, mutable_scalars=False,
|
||||
uselist=False, callable_=None,
|
||||
useobject=False, mutable_scalars=False,
|
||||
impl_class=None, backref=None, **kw):
|
||||
|
||||
manager = manager_of_class(class_)
|
||||
@@ -1281,7 +1301,7 @@ def register_attribute_impl(class_, key,
|
||||
manager.post_configure_attribute(key)
|
||||
return manager[key]
|
||||
|
||||
def register_descriptor(class_, key, comparator=None,
|
||||
def register_descriptor(class_, key, comparator=None,
|
||||
parententity=None, doc=None):
|
||||
manager = manager_of_class(class_)
|
||||
|
||||
@@ -1310,7 +1330,7 @@ def init_collection(obj, key):
|
||||
:func:`~sqlalchemy.orm.attributes.set_committed_value`.
|
||||
|
||||
obj is an instrumented object instance. An InstanceState
|
||||
is accepted directly for backwards compatibility but
|
||||
is accepted directly for backwards compatibility but
|
||||
this usage is deprecated.
|
||||
|
||||
"""
|
||||
@@ -1328,7 +1348,7 @@ def init_state_collection(state, dict_, key):
|
||||
def set_committed_value(instance, key, value):
|
||||
"""Set the value of an attribute with no history events.
|
||||
|
||||
Cancels any previous history present. The value should be
|
||||
Cancels any previous history present. The value should be
|
||||
a scalar value for scalar-holding attributes, or
|
||||
an iterable for any collection-holding attribute.
|
||||
|
||||
@@ -1385,7 +1405,7 @@ def del_attribute(instance, key):
|
||||
def flag_modified(instance, key):
|
||||
"""Mark an attribute on an instance as 'modified'.
|
||||
|
||||
This sets the 'modified' flag on the instance and
|
||||
This sets the 'modified' flag on the instance and
|
||||
establishes an unconditional change event for the given attribute.
|
||||
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# orm/collections.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -111,27 +111,62 @@ import weakref
|
||||
from sqlalchemy.sql import expression
|
||||
from sqlalchemy import schema, util, exc as sa_exc
|
||||
|
||||
|
||||
|
||||
__all__ = ['collection', 'collection_adapter',
|
||||
'mapped_collection', 'column_mapped_collection',
|
||||
'attribute_mapped_collection']
|
||||
|
||||
__instrumentation_mutex = util.threading.Lock()
|
||||
|
||||
class _SerializableColumnGetter(object):
|
||||
def __init__(self, colkeys):
|
||||
self.colkeys = colkeys
|
||||
self.composite = len(colkeys) > 1
|
||||
|
||||
class _PlainColumnGetter(object):
|
||||
"""Plain column getter, stores collection of Column objects
|
||||
directly.
|
||||
|
||||
Serializes to a :class:`._SerializableColumnGetterV2`
|
||||
which has more expensive __call__() performance
|
||||
and some rare caveats.
|
||||
|
||||
"""
|
||||
def __init__(self, cols):
|
||||
self.cols = cols
|
||||
self.composite = len(cols) > 1
|
||||
|
||||
def __reduce__(self):
|
||||
return _SerializableColumnGetter, (self.colkeys,)
|
||||
return _SerializableColumnGetterV2._reduce_from_cols(self.cols)
|
||||
|
||||
def _cols(self, mapper):
|
||||
return self.cols
|
||||
|
||||
def __call__(self, value):
|
||||
state = instance_state(value)
|
||||
m = _state_mapper(state)
|
||||
|
||||
key = [
|
||||
m._get_state_attr_by_column(state, state.dict, col)
|
||||
for col in self._cols(m)
|
||||
]
|
||||
|
||||
if self.composite:
|
||||
return tuple(key)
|
||||
else:
|
||||
return key[0]
|
||||
|
||||
class _SerializableColumnGetter(object):
|
||||
"""Column-based getter used in version 0.7.6 only.
|
||||
|
||||
Remains here for pickle compatibility with 0.7.6.
|
||||
|
||||
"""
|
||||
def __init__(self, colkeys):
|
||||
self.colkeys = colkeys
|
||||
self.composite = len(colkeys) > 1
|
||||
def __reduce__(self):
|
||||
return _SerializableColumnGetter, (self.colkeys,)
|
||||
def __call__(self, value):
|
||||
state = instance_state(value)
|
||||
m = _state_mapper(state)
|
||||
key = [m._get_state_attr_by_column(
|
||||
state, state.dict,
|
||||
state, state.dict,
|
||||
m.mapped_table.columns[k])
|
||||
for k in self.colkeys]
|
||||
if self.composite:
|
||||
@@ -139,6 +174,48 @@ class _SerializableColumnGetter(object):
|
||||
else:
|
||||
return key[0]
|
||||
|
||||
class _SerializableColumnGetterV2(_PlainColumnGetter):
|
||||
"""Updated serializable getter which deals with
|
||||
multi-table mapped classes.
|
||||
|
||||
Two extremely unusual cases are not supported.
|
||||
Mappings which have tables across multiple metadata
|
||||
objects, or which are mapped to non-Table selectables
|
||||
linked across inheriting mappers may fail to function
|
||||
here.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, colkeys):
|
||||
self.colkeys = colkeys
|
||||
self.composite = len(colkeys) > 1
|
||||
|
||||
def __reduce__(self):
|
||||
return self.__class__, (self.colkeys,)
|
||||
|
||||
@classmethod
|
||||
def _reduce_from_cols(cls, cols):
|
||||
def _table_key(c):
|
||||
if not isinstance(c.table, expression.TableClause):
|
||||
return None
|
||||
else:
|
||||
return c.table.key
|
||||
colkeys = [(c.key, _table_key(c)) for c in cols]
|
||||
return _SerializableColumnGetterV2, (colkeys,)
|
||||
|
||||
def _cols(self, mapper):
|
||||
cols = []
|
||||
metadata = getattr(mapper.local_table, 'metadata', None)
|
||||
for (ckey, tkey) in self.colkeys:
|
||||
if tkey is None or \
|
||||
metadata is None or \
|
||||
tkey not in metadata:
|
||||
cols.append(mapper.local_table.c[ckey])
|
||||
else:
|
||||
cols.append(metadata.tables[tkey].c[ckey])
|
||||
return cols
|
||||
|
||||
|
||||
def column_mapped_collection(mapping_spec):
|
||||
"""A dictionary-based collection type with column-based keying.
|
||||
|
||||
@@ -155,10 +232,10 @@ def column_mapped_collection(mapping_spec):
|
||||
from sqlalchemy.orm.util import _state_mapper
|
||||
from sqlalchemy.orm.attributes import instance_state
|
||||
|
||||
cols = [c.key for c in [
|
||||
expression._only_column_elements(q, "mapping_spec")
|
||||
for q in util.to_list(mapping_spec)]]
|
||||
keyfunc = _SerializableColumnGetter(cols)
|
||||
cols = [expression._only_column_elements(q, "mapping_spec")
|
||||
for q in util.to_list(mapping_spec)
|
||||
]
|
||||
keyfunc = _PlainColumnGetter(cols)
|
||||
return lambda: MappedCollection(keyfunc)
|
||||
|
||||
class _SerializableAttrGetter(object):
|
||||
@@ -632,8 +709,8 @@ class CollectionAdapter(object):
|
||||
"""
|
||||
if initiator is not False and item is not None:
|
||||
return self.attr.fire_append_event(
|
||||
self.owner_state,
|
||||
self.owner_state.dict,
|
||||
self.owner_state,
|
||||
self.owner_state.dict,
|
||||
item, initiator)
|
||||
else:
|
||||
return item
|
||||
@@ -648,8 +725,8 @@ class CollectionAdapter(object):
|
||||
"""
|
||||
if initiator is not False and item is not None:
|
||||
self.attr.fire_remove_event(
|
||||
self.owner_state,
|
||||
self.owner_state.dict,
|
||||
self.owner_state,
|
||||
self.owner_state.dict,
|
||||
item, initiator)
|
||||
|
||||
def fire_pre_remove_event(self, initiator=None):
|
||||
@@ -660,8 +737,8 @@ class CollectionAdapter(object):
|
||||
|
||||
"""
|
||||
self.attr.fire_pre_remove_event(
|
||||
self.owner_state,
|
||||
self.owner_state.dict,
|
||||
self.owner_state,
|
||||
self.owner_state.dict,
|
||||
initiator=initiator)
|
||||
|
||||
def __getstate__(self):
|
||||
|
||||
+200
-200
@@ -1,5 +1,5 @@
|
||||
# orm/dependency.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -48,7 +48,7 @@ class DependencyProcessor(object):
|
||||
|
||||
def hasparent(self, state):
|
||||
"""return True if the given object instance has a parent,
|
||||
according to the ``InstrumentedAttribute`` handled by this
|
||||
according to the ``InstrumentedAttribute`` handled by this
|
||||
``DependencyProcessor``.
|
||||
|
||||
"""
|
||||
@@ -69,29 +69,29 @@ class DependencyProcessor(object):
|
||||
before_delete = unitofwork.ProcessAll(uow, self, True, True)
|
||||
|
||||
parent_saves = unitofwork.SaveUpdateAll(
|
||||
uow,
|
||||
uow,
|
||||
self.parent.primary_base_mapper
|
||||
)
|
||||
child_saves = unitofwork.SaveUpdateAll(
|
||||
uow,
|
||||
uow,
|
||||
self.mapper.primary_base_mapper
|
||||
)
|
||||
|
||||
parent_deletes = unitofwork.DeleteAll(
|
||||
uow,
|
||||
uow,
|
||||
self.parent.primary_base_mapper
|
||||
)
|
||||
child_deletes = unitofwork.DeleteAll(
|
||||
uow,
|
||||
uow,
|
||||
self.mapper.primary_base_mapper
|
||||
)
|
||||
|
||||
self.per_property_dependencies(uow,
|
||||
parent_saves,
|
||||
child_saves,
|
||||
parent_deletes,
|
||||
child_deletes,
|
||||
after_save,
|
||||
self.per_property_dependencies(uow,
|
||||
parent_saves,
|
||||
child_saves,
|
||||
parent_deletes,
|
||||
child_deletes,
|
||||
after_save,
|
||||
before_delete
|
||||
)
|
||||
|
||||
@@ -99,7 +99,7 @@ class DependencyProcessor(object):
|
||||
def per_state_flush_actions(self, uow, states, isdelete):
|
||||
"""establish actions and dependencies related to a flush.
|
||||
|
||||
These actions will operate on all relevant states
|
||||
These actions will operate on all relevant states
|
||||
individually. This occurs only if there are cycles
|
||||
in the 'aggregated' version of events.
|
||||
|
||||
@@ -141,14 +141,14 @@ class DependencyProcessor(object):
|
||||
# check if the "parent" side is part of the cycle
|
||||
if not isdelete:
|
||||
parent_saves = unitofwork.SaveUpdateAll(
|
||||
uow,
|
||||
uow,
|
||||
self.parent.base_mapper)
|
||||
parent_deletes = before_delete = None
|
||||
if parent_saves in uow.cycles:
|
||||
parent_in_cycles = True
|
||||
else:
|
||||
parent_deletes = unitofwork.DeleteAll(
|
||||
uow,
|
||||
uow,
|
||||
self.parent.base_mapper)
|
||||
parent_saves = after_save = None
|
||||
if parent_deletes in uow.cycles:
|
||||
@@ -165,19 +165,19 @@ class DependencyProcessor(object):
|
||||
continue
|
||||
|
||||
if isdelete:
|
||||
before_delete = unitofwork.ProcessState(uow,
|
||||
before_delete = unitofwork.ProcessState(uow,
|
||||
self, True, state)
|
||||
if parent_in_cycles:
|
||||
parent_deletes = unitofwork.DeleteState(
|
||||
uow,
|
||||
state,
|
||||
uow,
|
||||
state,
|
||||
parent_base_mapper)
|
||||
else:
|
||||
after_save = unitofwork.ProcessState(uow, self, False, state)
|
||||
if parent_in_cycles:
|
||||
parent_saves = unitofwork.SaveUpdateState(
|
||||
uow,
|
||||
state,
|
||||
uow,
|
||||
state,
|
||||
parent_base_mapper)
|
||||
|
||||
if child_in_cycles:
|
||||
@@ -190,24 +190,24 @@ class DependencyProcessor(object):
|
||||
if deleted:
|
||||
child_action = (
|
||||
unitofwork.DeleteState(
|
||||
uow, child_state,
|
||||
child_base_mapper),
|
||||
uow, child_state,
|
||||
child_base_mapper),
|
||||
True)
|
||||
else:
|
||||
child_action = (
|
||||
unitofwork.SaveUpdateState(
|
||||
uow, child_state,
|
||||
child_base_mapper),
|
||||
uow, child_state,
|
||||
child_base_mapper),
|
||||
False)
|
||||
child_actions.append(child_action)
|
||||
|
||||
# establish dependencies between our possibly per-state
|
||||
# parent action and our possibly per-state child action.
|
||||
for child_action, childisdelete in child_actions:
|
||||
self.per_state_dependencies(uow, parent_saves,
|
||||
parent_deletes,
|
||||
child_action,
|
||||
after_save, before_delete,
|
||||
self.per_state_dependencies(uow, parent_saves,
|
||||
parent_deletes,
|
||||
child_action,
|
||||
after_save, before_delete,
|
||||
isdelete, childisdelete)
|
||||
|
||||
|
||||
@@ -232,12 +232,12 @@ class DependencyProcessor(object):
|
||||
passive = attributes.PASSIVE_OFF
|
||||
|
||||
for s in states:
|
||||
# TODO: add a high speed method
|
||||
# TODO: add a high speed method
|
||||
# to InstanceState which returns: attribute
|
||||
# has a non-None value, or had one
|
||||
history = uowcommit.get_attribute_history(
|
||||
s,
|
||||
self.key,
|
||||
s,
|
||||
self.key,
|
||||
passive)
|
||||
if history and not history.empty():
|
||||
return True
|
||||
@@ -248,7 +248,7 @@ class DependencyProcessor(object):
|
||||
|
||||
def _verify_canload(self, state):
|
||||
if state is not None and \
|
||||
not self.mapper._canload(state,
|
||||
not self.mapper._canload(state,
|
||||
allow_subtypes=not self.enable_typechecks):
|
||||
if self.mapper._canload(state, allow_subtypes=True):
|
||||
raise exc.FlushError('Attempting to flush an item of type '
|
||||
@@ -287,11 +287,11 @@ class DependencyProcessor(object):
|
||||
return None
|
||||
|
||||
process_key = tuple(sorted(
|
||||
[self.key] +
|
||||
[self.key] +
|
||||
[p.key for p in self.prop._reverse_property]
|
||||
))
|
||||
return uow.memo(
|
||||
('reverse_key', process_key),
|
||||
('reverse_key', process_key),
|
||||
set
|
||||
)
|
||||
|
||||
@@ -299,7 +299,7 @@ class DependencyProcessor(object):
|
||||
for x in related:
|
||||
if x is not None:
|
||||
uowcommit.issue_post_update(
|
||||
state,
|
||||
state,
|
||||
[r for l, r in self.prop.synchronize_pairs]
|
||||
)
|
||||
break
|
||||
@@ -312,21 +312,21 @@ class DependencyProcessor(object):
|
||||
|
||||
class OneToManyDP(DependencyProcessor):
|
||||
|
||||
def per_property_dependencies(self, uow, parent_saves,
|
||||
child_saves,
|
||||
parent_deletes,
|
||||
child_deletes,
|
||||
after_save,
|
||||
def per_property_dependencies(self, uow, parent_saves,
|
||||
child_saves,
|
||||
parent_deletes,
|
||||
child_deletes,
|
||||
after_save,
|
||||
before_delete,
|
||||
):
|
||||
if self.post_update:
|
||||
child_post_updates = unitofwork.IssuePostUpdate(
|
||||
uow,
|
||||
self.mapper.primary_base_mapper,
|
||||
uow,
|
||||
self.mapper.primary_base_mapper,
|
||||
False)
|
||||
child_pre_updates = unitofwork.IssuePostUpdate(
|
||||
uow,
|
||||
self.mapper.primary_base_mapper,
|
||||
uow,
|
||||
self.mapper.primary_base_mapper,
|
||||
True)
|
||||
|
||||
uow.dependencies.update([
|
||||
@@ -352,22 +352,22 @@ class OneToManyDP(DependencyProcessor):
|
||||
(before_delete, child_deletes),
|
||||
])
|
||||
|
||||
def per_state_dependencies(self, uow,
|
||||
save_parent,
|
||||
delete_parent,
|
||||
child_action,
|
||||
after_save, before_delete,
|
||||
def per_state_dependencies(self, uow,
|
||||
save_parent,
|
||||
delete_parent,
|
||||
child_action,
|
||||
after_save, before_delete,
|
||||
isdelete, childisdelete):
|
||||
|
||||
if self.post_update:
|
||||
|
||||
child_post_updates = unitofwork.IssuePostUpdate(
|
||||
uow,
|
||||
self.mapper.primary_base_mapper,
|
||||
uow,
|
||||
self.mapper.primary_base_mapper,
|
||||
False)
|
||||
child_pre_updates = unitofwork.IssuePostUpdate(
|
||||
uow,
|
||||
self.mapper.primary_base_mapper,
|
||||
uow,
|
||||
self.mapper.primary_base_mapper,
|
||||
True)
|
||||
|
||||
# TODO: this whole block is not covered
|
||||
@@ -393,7 +393,7 @@ class OneToManyDP(DependencyProcessor):
|
||||
else:
|
||||
uow.dependencies.update([
|
||||
(before_delete, child_pre_updates),
|
||||
(child_pre_updates, delete_parent),
|
||||
(child_pre_updates, delete_parent),
|
||||
])
|
||||
elif not isdelete:
|
||||
uow.dependencies.update([
|
||||
@@ -408,16 +408,16 @@ class OneToManyDP(DependencyProcessor):
|
||||
])
|
||||
|
||||
def presort_deletes(self, uowcommit, states):
|
||||
# head object is being deleted, and we manage its list of
|
||||
# child objects the child objects have to have their
|
||||
# head object is being deleted, and we manage its list of
|
||||
# child objects the child objects have to have their
|
||||
# foreign key to the parent set to NULL
|
||||
should_null_fks = not self.cascade.delete and \
|
||||
not self.passive_deletes == 'all'
|
||||
|
||||
for state in states:
|
||||
history = uowcommit.get_attribute_history(
|
||||
state,
|
||||
self.key,
|
||||
state,
|
||||
self.key,
|
||||
self._passive_delete_flag)
|
||||
if history:
|
||||
for child in history.deleted:
|
||||
@@ -430,7 +430,7 @@ class OneToManyDP(DependencyProcessor):
|
||||
if should_null_fks:
|
||||
for child in history.unchanged:
|
||||
if child is not None:
|
||||
uowcommit.register_object(child,
|
||||
uowcommit.register_object(child,
|
||||
operation="delete", prop=self.prop)
|
||||
|
||||
|
||||
@@ -447,25 +447,25 @@ class OneToManyDP(DependencyProcessor):
|
||||
passive = attributes.PASSIVE_OFF
|
||||
|
||||
history = uowcommit.get_attribute_history(
|
||||
state,
|
||||
self.key,
|
||||
state,
|
||||
self.key,
|
||||
passive)
|
||||
if history:
|
||||
for child in history.added:
|
||||
if child is not None:
|
||||
uowcommit.register_object(child, cancel_delete=True,
|
||||
operation="add",
|
||||
uowcommit.register_object(child, cancel_delete=True,
|
||||
operation="add",
|
||||
prop=self.prop)
|
||||
|
||||
children_added.update(history.added)
|
||||
|
||||
for child in history.deleted:
|
||||
if not self.cascade.delete_orphan:
|
||||
uowcommit.register_object(child, isdelete=False,
|
||||
operation='delete',
|
||||
uowcommit.register_object(child, isdelete=False,
|
||||
operation='delete',
|
||||
prop=self.prop)
|
||||
elif self.hasparent(child) is False:
|
||||
uowcommit.register_object(child, isdelete=True,
|
||||
uowcommit.register_object(child, isdelete=True,
|
||||
operation="delete", prop=self.prop)
|
||||
for c, m, st_, dct_ in self.mapper.cascade_iterator(
|
||||
'delete', child):
|
||||
@@ -478,16 +478,16 @@ class OneToManyDP(DependencyProcessor):
|
||||
for child in history.unchanged:
|
||||
if child is not None:
|
||||
uowcommit.register_object(
|
||||
child,
|
||||
False,
|
||||
child,
|
||||
False,
|
||||
self.passive_updates,
|
||||
operation="pk change",
|
||||
prop=self.prop)
|
||||
|
||||
def process_deletes(self, uowcommit, states):
|
||||
# head object is being deleted, and we manage its list of
|
||||
# child objects the child objects have to have their foreign
|
||||
# key to the parent set to NULL this phase can be called
|
||||
# head object is being deleted, and we manage its list of
|
||||
# child objects the child objects have to have their foreign
|
||||
# key to the parent set to NULL this phase can be called
|
||||
# safely for any cascade but is unnecessary if delete cascade
|
||||
# is on.
|
||||
|
||||
@@ -496,17 +496,17 @@ class OneToManyDP(DependencyProcessor):
|
||||
|
||||
for state in states:
|
||||
history = uowcommit.get_attribute_history(
|
||||
state,
|
||||
self.key,
|
||||
state,
|
||||
self.key,
|
||||
self._passive_delete_flag)
|
||||
if history:
|
||||
for child in history.deleted:
|
||||
if child is not None and \
|
||||
self.hasparent(child) is False:
|
||||
self._synchronize(
|
||||
state,
|
||||
child,
|
||||
None, True,
|
||||
state,
|
||||
child,
|
||||
None, True,
|
||||
uowcommit, False)
|
||||
if self.post_update and child:
|
||||
self._post_update(child, uowcommit, [state])
|
||||
@@ -516,18 +516,18 @@ class OneToManyDP(DependencyProcessor):
|
||||
difference(children_added):
|
||||
if child is not None:
|
||||
self._synchronize(
|
||||
state,
|
||||
child,
|
||||
None, True,
|
||||
state,
|
||||
child,
|
||||
None, True,
|
||||
uowcommit, False)
|
||||
if self.post_update and child:
|
||||
self._post_update(child,
|
||||
uowcommit,
|
||||
self._post_update(child,
|
||||
uowcommit,
|
||||
[state])
|
||||
|
||||
# technically, we can even remove each child from the
|
||||
# collection here too. but this would be a somewhat
|
||||
# inconsistent behavior since it wouldn't happen
|
||||
# collection here too. but this would be a somewhat
|
||||
# inconsistent behavior since it wouldn't happen
|
||||
#if the old parent wasn't deleted but child was moved.
|
||||
|
||||
def process_saves(self, uowcommit, states):
|
||||
@@ -538,7 +538,7 @@ class OneToManyDP(DependencyProcessor):
|
||||
attributes.PASSIVE_NO_INITIALIZE)
|
||||
if history:
|
||||
for child in history.added:
|
||||
self._synchronize(state, child, None,
|
||||
self._synchronize(state, child, None,
|
||||
False, uowcommit, False)
|
||||
if child is not None and self.post_update:
|
||||
self._post_update(child, uowcommit, [state])
|
||||
@@ -546,15 +546,15 @@ class OneToManyDP(DependencyProcessor):
|
||||
for child in history.deleted:
|
||||
if not self.cascade.delete_orphan and \
|
||||
not self.hasparent(child):
|
||||
self._synchronize(state, child, None, True,
|
||||
self._synchronize(state, child, None, True,
|
||||
uowcommit, False)
|
||||
|
||||
if self._pks_changed(uowcommit, state):
|
||||
for child in history.unchanged:
|
||||
self._synchronize(state, child, None,
|
||||
self._synchronize(state, child, None,
|
||||
False, uowcommit, True)
|
||||
|
||||
def _synchronize(self, state, child,
|
||||
def _synchronize(self, state, child,
|
||||
associationrow, clearkeys, uowcommit,
|
||||
pks_changed):
|
||||
source = state
|
||||
@@ -566,15 +566,15 @@ class OneToManyDP(DependencyProcessor):
|
||||
if clearkeys:
|
||||
sync.clear(dest, self.mapper, self.prop.synchronize_pairs)
|
||||
else:
|
||||
sync.populate(source, self.parent, dest, self.mapper,
|
||||
sync.populate(source, self.parent, dest, self.mapper,
|
||||
self.prop.synchronize_pairs, uowcommit,
|
||||
self.passive_updates and pks_changed)
|
||||
|
||||
def _pks_changed(self, uowcommit, state):
|
||||
return sync.source_modified(
|
||||
uowcommit,
|
||||
state,
|
||||
self.parent,
|
||||
uowcommit,
|
||||
state,
|
||||
self.parent,
|
||||
self.prop.synchronize_pairs)
|
||||
|
||||
class ManyToOneDP(DependencyProcessor):
|
||||
@@ -582,22 +582,22 @@ class ManyToOneDP(DependencyProcessor):
|
||||
DependencyProcessor.__init__(self, prop)
|
||||
self.mapper._dependency_processors.append(DetectKeySwitch(prop))
|
||||
|
||||
def per_property_dependencies(self, uow,
|
||||
parent_saves,
|
||||
child_saves,
|
||||
parent_deletes,
|
||||
child_deletes,
|
||||
after_save,
|
||||
def per_property_dependencies(self, uow,
|
||||
parent_saves,
|
||||
child_saves,
|
||||
parent_deletes,
|
||||
child_deletes,
|
||||
after_save,
|
||||
before_delete):
|
||||
|
||||
if self.post_update:
|
||||
parent_post_updates = unitofwork.IssuePostUpdate(
|
||||
uow,
|
||||
self.parent.primary_base_mapper,
|
||||
uow,
|
||||
self.parent.primary_base_mapper,
|
||||
False)
|
||||
parent_pre_updates = unitofwork.IssuePostUpdate(
|
||||
uow,
|
||||
self.parent.primary_base_mapper,
|
||||
uow,
|
||||
self.parent.primary_base_mapper,
|
||||
True)
|
||||
|
||||
uow.dependencies.update([
|
||||
@@ -618,19 +618,19 @@ class ManyToOneDP(DependencyProcessor):
|
||||
(parent_deletes, child_deletes)
|
||||
])
|
||||
|
||||
def per_state_dependencies(self, uow,
|
||||
save_parent,
|
||||
delete_parent,
|
||||
child_action,
|
||||
after_save, before_delete,
|
||||
def per_state_dependencies(self, uow,
|
||||
save_parent,
|
||||
delete_parent,
|
||||
child_action,
|
||||
after_save, before_delete,
|
||||
isdelete, childisdelete):
|
||||
|
||||
if self.post_update:
|
||||
|
||||
if not isdelete:
|
||||
parent_post_updates = unitofwork.IssuePostUpdate(
|
||||
uow,
|
||||
self.parent.primary_base_mapper,
|
||||
uow,
|
||||
self.parent.primary_base_mapper,
|
||||
False)
|
||||
if childisdelete:
|
||||
uow.dependencies.update([
|
||||
@@ -646,8 +646,8 @@ class ManyToOneDP(DependencyProcessor):
|
||||
])
|
||||
else:
|
||||
parent_pre_updates = unitofwork.IssuePostUpdate(
|
||||
uow,
|
||||
self.parent.primary_base_mapper,
|
||||
uow,
|
||||
self.parent.primary_base_mapper,
|
||||
True)
|
||||
|
||||
uow.dependencies.update([
|
||||
@@ -677,8 +677,8 @@ class ManyToOneDP(DependencyProcessor):
|
||||
if self.cascade.delete or self.cascade.delete_orphan:
|
||||
for state in states:
|
||||
history = uowcommit.get_attribute_history(
|
||||
state,
|
||||
self.key,
|
||||
state,
|
||||
self.key,
|
||||
self._passive_delete_flag)
|
||||
if history:
|
||||
if self.cascade.delete_orphan:
|
||||
@@ -688,7 +688,7 @@ class ManyToOneDP(DependencyProcessor):
|
||||
for child in todelete:
|
||||
if child is None:
|
||||
continue
|
||||
uowcommit.register_object(child, isdelete=True,
|
||||
uowcommit.register_object(child, isdelete=True,
|
||||
operation="delete", prop=self.prop)
|
||||
for c, m, st_, dct_ in self.mapper.cascade_iterator(
|
||||
'delete', child):
|
||||
@@ -700,14 +700,14 @@ class ManyToOneDP(DependencyProcessor):
|
||||
uowcommit.register_object(state, operation="add", prop=self.prop)
|
||||
if self.cascade.delete_orphan:
|
||||
history = uowcommit.get_attribute_history(
|
||||
state,
|
||||
self.key,
|
||||
state,
|
||||
self.key,
|
||||
self._passive_delete_flag)
|
||||
if history:
|
||||
ret = True
|
||||
for child in history.deleted:
|
||||
if self.hasparent(child) is False:
|
||||
uowcommit.register_object(child, isdelete=True,
|
||||
uowcommit.register_object(child, isdelete=True,
|
||||
operation="delete", prop=self.prop)
|
||||
|
||||
for c, m, st_, dct_ in self.mapper.cascade_iterator(
|
||||
@@ -721,15 +721,15 @@ class ManyToOneDP(DependencyProcessor):
|
||||
not self.cascade.delete_orphan and \
|
||||
not self.passive_deletes == 'all':
|
||||
|
||||
# post_update means we have to update our
|
||||
# post_update means we have to update our
|
||||
# row to not reference the child object
|
||||
# before we can DELETE the row
|
||||
for state in states:
|
||||
self._synchronize(state, None, None, True, uowcommit)
|
||||
if state and self.post_update:
|
||||
history = uowcommit.get_attribute_history(
|
||||
state,
|
||||
self.key,
|
||||
state,
|
||||
self.key,
|
||||
self._passive_delete_flag)
|
||||
if history:
|
||||
self._post_update(state, uowcommit, history.sum())
|
||||
@@ -737,12 +737,12 @@ class ManyToOneDP(DependencyProcessor):
|
||||
def process_saves(self, uowcommit, states):
|
||||
for state in states:
|
||||
history = uowcommit.get_attribute_history(
|
||||
state,
|
||||
state,
|
||||
self.key,
|
||||
attributes.PASSIVE_NO_INITIALIZE)
|
||||
if history:
|
||||
for child in history.added:
|
||||
self._synchronize(state, child, None, False,
|
||||
self._synchronize(state, child, None, False,
|
||||
uowcommit, "add")
|
||||
|
||||
if self.post_update:
|
||||
@@ -759,7 +759,7 @@ class ManyToOneDP(DependencyProcessor):
|
||||
not uowcommit.session._contains_state(child):
|
||||
util.warn(
|
||||
"Object of type %s not in session, %s "
|
||||
"operation along '%s' won't proceed" %
|
||||
"operation along '%s' won't proceed" %
|
||||
(mapperutil.state_class_str(child), operation, self.prop))
|
||||
return
|
||||
|
||||
@@ -767,14 +767,14 @@ class ManyToOneDP(DependencyProcessor):
|
||||
sync.clear(state, self.parent, self.prop.synchronize_pairs)
|
||||
else:
|
||||
self._verify_canload(child)
|
||||
sync.populate(child, self.mapper, state,
|
||||
self.parent,
|
||||
self.prop.synchronize_pairs,
|
||||
sync.populate(child, self.mapper, state,
|
||||
self.parent,
|
||||
self.prop.synchronize_pairs,
|
||||
uowcommit,
|
||||
False)
|
||||
False)
|
||||
|
||||
class DetectKeySwitch(DependencyProcessor):
|
||||
"""For many-to-one relationships with no one-to-many backref,
|
||||
"""For many-to-one relationships with no one-to-many backref,
|
||||
searches for parents through the unit of work when a primary
|
||||
key has changed and updates them.
|
||||
|
||||
@@ -798,7 +798,7 @@ class DetectKeySwitch(DependencyProcessor):
|
||||
|
||||
def per_property_flush_actions(self, uow):
|
||||
parent_saves = unitofwork.SaveUpdateAll(
|
||||
uow,
|
||||
uow,
|
||||
self.parent.base_mapper)
|
||||
after_save = unitofwork.ProcessAll(uow, self, False, False)
|
||||
uow.dependencies.update([
|
||||
@@ -837,7 +837,7 @@ class DetectKeySwitch(DependencyProcessor):
|
||||
|
||||
def _key_switchers(self, uow, states):
|
||||
switched, notswitched = uow.memo(
|
||||
('pk_switchers', self),
|
||||
('pk_switchers', self),
|
||||
lambda: (set(), set())
|
||||
)
|
||||
|
||||
@@ -865,29 +865,29 @@ class DetectKeySwitch(DependencyProcessor):
|
||||
related is not None:
|
||||
related_state = attributes.instance_state(dict_[self.key])
|
||||
if related_state in switchers:
|
||||
uowcommit.register_object(state,
|
||||
False,
|
||||
uowcommit.register_object(state,
|
||||
False,
|
||||
self.passive_updates)
|
||||
sync.populate(
|
||||
related_state,
|
||||
self.mapper, state,
|
||||
self.parent, self.prop.synchronize_pairs,
|
||||
related_state,
|
||||
self.mapper, state,
|
||||
self.parent, self.prop.synchronize_pairs,
|
||||
uowcommit, self.passive_updates)
|
||||
|
||||
def _pks_changed(self, uowcommit, state):
|
||||
return bool(state.key) and sync.source_modified(uowcommit,
|
||||
state,
|
||||
self.mapper,
|
||||
return bool(state.key) and sync.source_modified(uowcommit,
|
||||
state,
|
||||
self.mapper,
|
||||
self.prop.synchronize_pairs)
|
||||
|
||||
|
||||
class ManyToManyDP(DependencyProcessor):
|
||||
|
||||
def per_property_dependencies(self, uow, parent_saves,
|
||||
child_saves,
|
||||
parent_deletes,
|
||||
child_deletes,
|
||||
after_save,
|
||||
def per_property_dependencies(self, uow, parent_saves,
|
||||
child_saves,
|
||||
parent_deletes,
|
||||
child_deletes,
|
||||
after_save,
|
||||
before_delete
|
||||
):
|
||||
|
||||
@@ -896,9 +896,9 @@ class ManyToManyDP(DependencyProcessor):
|
||||
(child_saves, after_save),
|
||||
(after_save, child_deletes),
|
||||
|
||||
# a rowswitch on the parent from deleted to saved
|
||||
# can make this one occur, as the "save" may remove
|
||||
# an element from the
|
||||
# a rowswitch on the parent from deleted to saved
|
||||
# can make this one occur, as the "save" may remove
|
||||
# an element from the
|
||||
# "deleted" list before we have a chance to
|
||||
# process its child rows
|
||||
(before_delete, parent_saves),
|
||||
@@ -908,11 +908,11 @@ class ManyToManyDP(DependencyProcessor):
|
||||
(before_delete, child_saves),
|
||||
])
|
||||
|
||||
def per_state_dependencies(self, uow,
|
||||
save_parent,
|
||||
delete_parent,
|
||||
child_action,
|
||||
after_save, before_delete,
|
||||
def per_state_dependencies(self, uow,
|
||||
save_parent,
|
||||
delete_parent,
|
||||
child_action,
|
||||
after_save, before_delete,
|
||||
isdelete, childisdelete):
|
||||
if not isdelete:
|
||||
if childisdelete:
|
||||
@@ -933,25 +933,25 @@ class ManyToManyDP(DependencyProcessor):
|
||||
|
||||
def presort_deletes(self, uowcommit, states):
|
||||
if not self.passive_deletes:
|
||||
# if no passive deletes, load history on
|
||||
# if no passive deletes, load history on
|
||||
# the collection, so that prop_has_changes()
|
||||
# returns True
|
||||
for state in states:
|
||||
history = uowcommit.get_attribute_history(
|
||||
state,
|
||||
self.key,
|
||||
state,
|
||||
self.key,
|
||||
self._passive_delete_flag)
|
||||
|
||||
def presort_saves(self, uowcommit, states):
|
||||
if not self.passive_updates:
|
||||
# if no passive updates, load history on
|
||||
# if no passive updates, load history on
|
||||
# each collection where parent has changed PK,
|
||||
# so that prop_has_changes() returns True
|
||||
for state in states:
|
||||
if self._pks_changed(uowcommit, state):
|
||||
history = uowcommit.get_attribute_history(
|
||||
state,
|
||||
self.key,
|
||||
state,
|
||||
self.key,
|
||||
attributes.PASSIVE_OFF)
|
||||
|
||||
if not self.cascade.delete_orphan:
|
||||
@@ -961,16 +961,16 @@ class ManyToManyDP(DependencyProcessor):
|
||||
# if delete_orphan check is turned on.
|
||||
for state in states:
|
||||
history = uowcommit.get_attribute_history(
|
||||
state,
|
||||
self.key,
|
||||
state,
|
||||
self.key,
|
||||
attributes.PASSIVE_NO_INITIALIZE)
|
||||
if history:
|
||||
for child in history.deleted:
|
||||
if self.hasparent(child) is False:
|
||||
uowcommit.register_object(child, isdelete=True,
|
||||
uowcommit.register_object(child, isdelete=True,
|
||||
operation="delete", prop=self.prop)
|
||||
for c, m, st_, dct_ in self.mapper.cascade_iterator(
|
||||
'delete',
|
||||
'delete',
|
||||
child):
|
||||
uowcommit.register_object(
|
||||
st_, isdelete=True)
|
||||
@@ -983,23 +983,23 @@ class ManyToManyDP(DependencyProcessor):
|
||||
processed = self._get_reversed_processed_set(uowcommit)
|
||||
tmp = set()
|
||||
for state in states:
|
||||
# this history should be cached already, as
|
||||
# this history should be cached already, as
|
||||
# we loaded it in preprocess_deletes
|
||||
history = uowcommit.get_attribute_history(
|
||||
state,
|
||||
self.key,
|
||||
state,
|
||||
self.key,
|
||||
self._passive_delete_flag)
|
||||
if history:
|
||||
for child in history.non_added():
|
||||
if child is None or \
|
||||
(processed is not None and
|
||||
(processed is not None and
|
||||
(state, child) in processed):
|
||||
continue
|
||||
associationrow = {}
|
||||
if not self._synchronize(
|
||||
state,
|
||||
child,
|
||||
associationrow,
|
||||
state,
|
||||
child,
|
||||
associationrow,
|
||||
False, uowcommit, "delete"):
|
||||
continue
|
||||
secondary_delete.append(associationrow)
|
||||
@@ -1009,7 +1009,7 @@ class ManyToManyDP(DependencyProcessor):
|
||||
if processed is not None:
|
||||
processed.update(tmp)
|
||||
|
||||
self._run_crud(uowcommit, secondary_insert,
|
||||
self._run_crud(uowcommit, secondary_insert,
|
||||
secondary_update, secondary_delete)
|
||||
|
||||
def process_saves(self, uowcommit, states):
|
||||
@@ -1022,7 +1022,7 @@ class ManyToManyDP(DependencyProcessor):
|
||||
|
||||
for state in states:
|
||||
need_cascade_pks = not self.passive_updates and \
|
||||
self._pks_changed(uowcommit, state)
|
||||
self._pks_changed(uowcommit, state)
|
||||
if need_cascade_pks:
|
||||
passive = attributes.PASSIVE_OFF
|
||||
else:
|
||||
@@ -1032,45 +1032,45 @@ class ManyToManyDP(DependencyProcessor):
|
||||
if history:
|
||||
for child in history.added:
|
||||
if child is None or \
|
||||
(processed is not None and
|
||||
(processed is not None and
|
||||
(state, child) in processed):
|
||||
continue
|
||||
associationrow = {}
|
||||
if not self._synchronize(state,
|
||||
child,
|
||||
associationrow,
|
||||
if not self._synchronize(state,
|
||||
child,
|
||||
associationrow,
|
||||
False, uowcommit, "add"):
|
||||
continue
|
||||
secondary_insert.append(associationrow)
|
||||
for child in history.deleted:
|
||||
if child is None or \
|
||||
(processed is not None and
|
||||
(processed is not None and
|
||||
(state, child) in processed):
|
||||
continue
|
||||
associationrow = {}
|
||||
if not self._synchronize(state,
|
||||
child,
|
||||
associationrow,
|
||||
if not self._synchronize(state,
|
||||
child,
|
||||
associationrow,
|
||||
False, uowcommit, "delete"):
|
||||
continue
|
||||
secondary_delete.append(associationrow)
|
||||
|
||||
tmp.update((c, state)
|
||||
tmp.update((c, state)
|
||||
for c in history.added + history.deleted)
|
||||
|
||||
if need_cascade_pks:
|
||||
|
||||
for child in history.unchanged:
|
||||
associationrow = {}
|
||||
sync.update(state,
|
||||
self.parent,
|
||||
associationrow,
|
||||
"old_",
|
||||
sync.update(state,
|
||||
self.parent,
|
||||
associationrow,
|
||||
"old_",
|
||||
self.prop.synchronize_pairs)
|
||||
sync.update(child,
|
||||
self.mapper,
|
||||
associationrow,
|
||||
"old_",
|
||||
sync.update(child,
|
||||
self.mapper,
|
||||
associationrow,
|
||||
"old_",
|
||||
self.prop.secondary_synchronize_pairs)
|
||||
|
||||
secondary_update.append(associationrow)
|
||||
@@ -1078,18 +1078,18 @@ class ManyToManyDP(DependencyProcessor):
|
||||
if processed is not None:
|
||||
processed.update(tmp)
|
||||
|
||||
self._run_crud(uowcommit, secondary_insert,
|
||||
self._run_crud(uowcommit, secondary_insert,
|
||||
secondary_update, secondary_delete)
|
||||
|
||||
def _run_crud(self, uowcommit, secondary_insert,
|
||||
def _run_crud(self, uowcommit, secondary_insert,
|
||||
secondary_update, secondary_delete):
|
||||
connection = uowcommit.transaction.connection(self.mapper)
|
||||
|
||||
if secondary_delete:
|
||||
associationrow = secondary_delete[0]
|
||||
statement = self.secondary.delete(sql.and_(*[
|
||||
c == sql.bindparam(c.key, type_=c.type)
|
||||
for c in self.secondary.c
|
||||
c == sql.bindparam(c.key, type_=c.type)
|
||||
for c in self.secondary.c
|
||||
if c.key in associationrow
|
||||
]))
|
||||
result = connection.execute(statement, secondary_delete)
|
||||
@@ -1098,7 +1098,7 @@ class ManyToManyDP(DependencyProcessor):
|
||||
result.rowcount != len(secondary_delete):
|
||||
raise exc.StaleDataError(
|
||||
"DELETE statement on table '%s' expected to delete %d row(s); "
|
||||
"Only %d were matched." %
|
||||
"Only %d were matched." %
|
||||
(self.secondary.description, len(secondary_delete),
|
||||
result.rowcount)
|
||||
)
|
||||
@@ -1106,8 +1106,8 @@ class ManyToManyDP(DependencyProcessor):
|
||||
if secondary_update:
|
||||
associationrow = secondary_update[0]
|
||||
statement = self.secondary.update(sql.and_(*[
|
||||
c == sql.bindparam("old_" + c.key, type_=c.type)
|
||||
for c in self.secondary.c
|
||||
c == sql.bindparam("old_" + c.key, type_=c.type)
|
||||
for c in self.secondary.c
|
||||
if c.key in associationrow
|
||||
]))
|
||||
result = connection.execute(statement, secondary_update)
|
||||
@@ -1115,7 +1115,7 @@ class ManyToManyDP(DependencyProcessor):
|
||||
result.rowcount != len(secondary_update):
|
||||
raise exc.StaleDataError(
|
||||
"UPDATE statement on table '%s' expected to update %d row(s); "
|
||||
"Only %d were matched." %
|
||||
"Only %d were matched." %
|
||||
(self.secondary.description, len(secondary_update),
|
||||
result.rowcount)
|
||||
)
|
||||
@@ -1124,7 +1124,7 @@ class ManyToManyDP(DependencyProcessor):
|
||||
statement = self.secondary.insert()
|
||||
connection.execute(statement, secondary_insert)
|
||||
|
||||
def _synchronize(self, state, child, associationrow,
|
||||
def _synchronize(self, state, child, associationrow,
|
||||
clearkeys, uowcommit, operation):
|
||||
if associationrow is None:
|
||||
return
|
||||
@@ -1133,13 +1133,13 @@ class ManyToManyDP(DependencyProcessor):
|
||||
if not child.deleted:
|
||||
util.warn(
|
||||
"Object of type %s not in session, %s "
|
||||
"operation along '%s' won't proceed" %
|
||||
"operation along '%s' won't proceed" %
|
||||
(mapperutil.state_class_str(child), operation, self.prop))
|
||||
return False
|
||||
|
||||
self._verify_canload(child)
|
||||
|
||||
sync.populate_dict(state, self.parent, associationrow,
|
||||
sync.populate_dict(state, self.parent, associationrow,
|
||||
self.prop.synchronize_pairs)
|
||||
sync.populate_dict(child, self.mapper, associationrow,
|
||||
self.prop.secondary_synchronize_pairs)
|
||||
@@ -1148,9 +1148,9 @@ class ManyToManyDP(DependencyProcessor):
|
||||
|
||||
def _pks_changed(self, uowcommit, state):
|
||||
return sync.source_modified(
|
||||
uowcommit,
|
||||
state,
|
||||
self.parent,
|
||||
uowcommit,
|
||||
state,
|
||||
self.parent,
|
||||
self.prop.synchronize_pairs)
|
||||
|
||||
_direction_to_processor = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# orm/deprecated_interfaces.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -11,10 +11,10 @@ from interfaces import EXT_CONTINUE
|
||||
class MapperExtension(object):
|
||||
"""Base implementation for :class:`.Mapper` event hooks.
|
||||
|
||||
.. note::
|
||||
|
||||
.. note::
|
||||
|
||||
:class:`.MapperExtension` is deprecated. Please
|
||||
refer to :func:`.event.listen` as well as
|
||||
refer to :func:`.event.listen` as well as
|
||||
:class:`.MapperEvents`.
|
||||
|
||||
New extension classes subclass :class:`.MapperExtension` and are specified
|
||||
@@ -42,8 +42,8 @@ class MapperExtension(object):
|
||||
to the next ``MapperExtension`` for processing". For methods
|
||||
that return objects like translated rows or new object
|
||||
instances, EXT_CONTINUE means the result of the method
|
||||
should be ignored. In some cases it's required for a
|
||||
default mapper activity to be performed, such as adding a
|
||||
should be ignored. In some cases it's required for a
|
||||
default mapper activity to be performed, such as adding a
|
||||
new instance to a result list.
|
||||
|
||||
The symbol EXT_STOP has significance within a chain
|
||||
@@ -91,29 +91,29 @@ class MapperExtension(object):
|
||||
def reconstruct(instance, ctx):
|
||||
ls_meth(self, instance)
|
||||
return reconstruct
|
||||
event.listen(self.class_manager, 'load',
|
||||
event.listen(self.class_manager, 'load',
|
||||
go(ls_meth), raw=False, propagate=True)
|
||||
elif meth == 'init_instance':
|
||||
def go(ls_meth):
|
||||
def init_instance(instance, args, kwargs):
|
||||
ls_meth(self, self.class_,
|
||||
self.class_manager.original_init,
|
||||
ls_meth(self, self.class_,
|
||||
self.class_manager.original_init,
|
||||
instance, args, kwargs)
|
||||
return init_instance
|
||||
event.listen(self.class_manager, 'init',
|
||||
event.listen(self.class_manager, 'init',
|
||||
go(ls_meth), raw=False, propagate=True)
|
||||
elif meth == 'init_failed':
|
||||
def go(ls_meth):
|
||||
def init_failed(instance, args, kwargs):
|
||||
util.warn_exception(ls_meth, self, self.class_,
|
||||
self.class_manager.original_init,
|
||||
util.warn_exception(ls_meth, self, self.class_,
|
||||
self.class_manager.original_init,
|
||||
instance, args, kwargs)
|
||||
|
||||
return init_failed
|
||||
event.listen(self.class_manager, 'init_failure',
|
||||
event.listen(self.class_manager, 'init_failure',
|
||||
go(ls_meth), raw=False, propagate=True)
|
||||
else:
|
||||
event.listen(self, "%s" % meth, ls_meth,
|
||||
event.listen(self, "%s" % meth, ls_meth,
|
||||
raw=False, retval=True, propagate=True)
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ class MapperExtension(object):
|
||||
"""Receive a class when the mapper is first constructed, and has
|
||||
applied instrumentation to the mapped class.
|
||||
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
chain; the parent mapper's behavior isn't modified by this method.
|
||||
|
||||
"""
|
||||
@@ -130,25 +130,25 @@ class MapperExtension(object):
|
||||
def init_instance(self, mapper, class_, oldinit, instance, args, kwargs):
|
||||
"""Receive an instance when it's constructor is called.
|
||||
|
||||
This method is only called during a userland construction of
|
||||
This method is only called during a userland construction of
|
||||
an object. It is not called when an object is loaded from the
|
||||
database.
|
||||
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
chain; the parent mapper's behavior isn't modified by this method.
|
||||
|
||||
"""
|
||||
return EXT_CONTINUE
|
||||
|
||||
def init_failed(self, mapper, class_, oldinit, instance, args, kwargs):
|
||||
"""Receive an instance when it's constructor has been called,
|
||||
"""Receive an instance when it's constructor has been called,
|
||||
and raised an exception.
|
||||
|
||||
This method is only called during a userland construction of
|
||||
This method is only called during a userland construction of
|
||||
an object. It is not called when an object is loaded from the
|
||||
database.
|
||||
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
chain; the parent mapper's behavior isn't modified by this method.
|
||||
|
||||
"""
|
||||
@@ -160,9 +160,9 @@ class MapperExtension(object):
|
||||
|
||||
This is called when the mapper first receives a row, before
|
||||
the object identity or the instance itself has been derived
|
||||
from that row. The given row may or may not be a
|
||||
from that row. The given row may or may not be a
|
||||
``RowProxy`` object - it will always be a dictionary-like
|
||||
object which contains mapped columns as keys. The
|
||||
object which contains mapped columns as keys. The
|
||||
returned object should also be a dictionary-like object
|
||||
which recognizes mapped columns as keys.
|
||||
|
||||
@@ -197,7 +197,7 @@ class MapperExtension(object):
|
||||
"""
|
||||
return EXT_CONTINUE
|
||||
|
||||
def append_result(self, mapper, selectcontext, row, instance,
|
||||
def append_result(self, mapper, selectcontext, row, instance,
|
||||
result, **flags):
|
||||
"""Receive an object instance before that instance is appended
|
||||
to a result list.
|
||||
@@ -231,7 +231,7 @@ class MapperExtension(object):
|
||||
|
||||
return EXT_CONTINUE
|
||||
|
||||
def populate_instance(self, mapper, selectcontext, row,
|
||||
def populate_instance(self, mapper, selectcontext, row,
|
||||
instance, **flags):
|
||||
"""Receive an instance before that instance has
|
||||
its attributes populated.
|
||||
@@ -247,10 +247,11 @@ class MapperExtension(object):
|
||||
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
|
||||
``reconstruct_instance()``, or the ``@orm.reconstructor``
|
||||
decorator.
|
||||
.. deprecated:: 0.5
|
||||
Most usages of this hook are obsolete. For a
|
||||
generic "object has been newly created from a row" hook, use
|
||||
``reconstruct_instance()``, or the ``@orm.reconstructor``
|
||||
decorator.
|
||||
|
||||
"""
|
||||
return EXT_CONTINUE
|
||||
@@ -265,11 +266,11 @@ class MapperExtension(object):
|
||||
instance's lifetime.
|
||||
|
||||
Note that during a result-row load, this method is called upon
|
||||
the first row received for this instance. Note that some
|
||||
attributes and collections may or may not be loaded or even
|
||||
the first row received for this instance. Note that some
|
||||
attributes and collections may or may not be loaded or even
|
||||
initialized, depending on what's present in the result rows.
|
||||
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
chain; the parent mapper's behavior isn't modified by this method.
|
||||
|
||||
"""
|
||||
@@ -284,12 +285,12 @@ class MapperExtension(object):
|
||||
|
||||
Column-based attributes can be modified within this method
|
||||
which will result in the new value being inserted. However
|
||||
*no* changes to the overall flush plan can be made, and
|
||||
*no* changes to the overall flush plan can be made, and
|
||||
manipulation of the ``Session`` will not have the desired effect.
|
||||
To manipulate the ``Session`` within an extension, use
|
||||
To manipulate the ``Session`` within an extension, use
|
||||
``SessionExtension``.
|
||||
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
chain; the parent mapper's behavior isn't modified by this method.
|
||||
|
||||
"""
|
||||
@@ -299,7 +300,7 @@ class MapperExtension(object):
|
||||
def after_insert(self, mapper, connection, instance):
|
||||
"""Receive an object instance after that instance is inserted.
|
||||
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
chain; the parent mapper's behavior isn't modified by this method.
|
||||
|
||||
"""
|
||||
@@ -326,12 +327,12 @@ class MapperExtension(object):
|
||||
|
||||
Column-based attributes can be modified within this method
|
||||
which will result in the new value being updated. However
|
||||
*no* changes to the overall flush plan can be made, and
|
||||
*no* changes to the overall flush plan can be made, and
|
||||
manipulation of the ``Session`` will not have the desired effect.
|
||||
To manipulate the ``Session`` within an extension, use
|
||||
To manipulate the ``Session`` within an extension, use
|
||||
``SessionExtension``.
|
||||
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
chain; the parent mapper's behavior isn't modified by this method.
|
||||
|
||||
"""
|
||||
@@ -341,7 +342,7 @@ class MapperExtension(object):
|
||||
def after_update(self, mapper, connection, instance):
|
||||
"""Receive an object instance after that instance is updated.
|
||||
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
chain; the parent mapper's behavior isn't modified by this method.
|
||||
|
||||
"""
|
||||
@@ -356,7 +357,7 @@ class MapperExtension(object):
|
||||
desired effect. To manipulate the ``Session`` within an
|
||||
extension, use ``SessionExtension``.
|
||||
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
The return value is only significant within the ``MapperExtension``
|
||||
chain; the parent mapper's behavior isn't modified by this method.
|
||||
|
||||
"""
|
||||
@@ -377,10 +378,10 @@ class SessionExtension(object):
|
||||
|
||||
"""Base implementation for :class:`.Session` event hooks.
|
||||
|
||||
.. note::
|
||||
|
||||
.. note::
|
||||
|
||||
:class:`.SessionExtension` is deprecated. Please
|
||||
refer to :func:`.event.listen` as well as
|
||||
refer to :func:`.event.listen` as well as
|
||||
:class:`.SessionEvents`.
|
||||
|
||||
Subclasses may be installed into a :class:`.Session` (or
|
||||
@@ -497,10 +498,10 @@ class AttributeExtension(object):
|
||||
"""Base implementation for :class:`.AttributeImpl` event hooks, events
|
||||
that fire upon attribute mutations in user code.
|
||||
|
||||
.. note::
|
||||
|
||||
.. note::
|
||||
|
||||
:class:`.AttributeExtension` is deprecated. Please
|
||||
refer to :func:`.event.listen` as well as
|
||||
refer to :func:`.event.listen` as well as
|
||||
:class:`.AttributeEvents`.
|
||||
|
||||
:class:`.AttributeExtension` is used to listen for set,
|
||||
@@ -554,10 +555,10 @@ class AttributeExtension(object):
|
||||
active_history=listener.active_history,
|
||||
raw=True, retval=True)
|
||||
event.listen(self, 'remove', listener.remove,
|
||||
active_history=listener.active_history,
|
||||
active_history=listener.active_history,
|
||||
raw=True, retval=True)
|
||||
event.listen(self, 'set', listener.set,
|
||||
active_history=listener.active_history,
|
||||
active_history=listener.active_history,
|
||||
raw=True, retval=True)
|
||||
|
||||
def append(self, state, value, initiator):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# orm/descriptor_props.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -19,7 +19,7 @@ from sqlalchemy.sql import expression
|
||||
properties = util.importlater('sqlalchemy.orm', 'properties')
|
||||
|
||||
class DescriptorProperty(MapperProperty):
|
||||
""":class:`.MapperProperty` which proxies access to a
|
||||
""":class:`.MapperProperty` which proxies access to a
|
||||
user-defined descriptor."""
|
||||
|
||||
doc = None
|
||||
@@ -35,7 +35,7 @@ class DescriptorProperty(MapperProperty):
|
||||
self.key = key
|
||||
|
||||
if hasattr(prop, 'get_history'):
|
||||
def get_history(self, state, dict_,
|
||||
def get_history(self, state, dict_,
|
||||
passive=attributes.PASSIVE_OFF):
|
||||
return prop.get_history(state, dict_, passive)
|
||||
|
||||
@@ -62,7 +62,7 @@ class DescriptorProperty(MapperProperty):
|
||||
create_proxied_attribute(self.descriptor)\
|
||||
(
|
||||
self.parent.class_,
|
||||
self.key,
|
||||
self.key,
|
||||
self.descriptor,
|
||||
lambda: self._comparator_factory(mapper),
|
||||
doc=self.doc
|
||||
@@ -89,7 +89,7 @@ class CompositeProperty(DescriptorProperty):
|
||||
self._setup_event_handlers()
|
||||
|
||||
def do_init(self):
|
||||
"""Initialization which occurs after the :class:`.CompositeProperty`
|
||||
"""Initialization which occurs after the :class:`.CompositeProperty`
|
||||
has been associated with its parent mapper.
|
||||
|
||||
"""
|
||||
@@ -97,7 +97,7 @@ class CompositeProperty(DescriptorProperty):
|
||||
self._setup_arguments_on_columns()
|
||||
|
||||
def _create_descriptor(self):
|
||||
"""Create the Python descriptor that will serve as
|
||||
"""Create the Python descriptor that will serve as
|
||||
the access point on instances of the mapped class.
|
||||
|
||||
"""
|
||||
@@ -113,12 +113,12 @@ class CompositeProperty(DescriptorProperty):
|
||||
values = [getattr(instance, key) for key in self._attribute_keys]
|
||||
|
||||
# current expected behavior here is that the composite is
|
||||
# created on access if the object is persistent or if
|
||||
# col attributes have non-None. This would be better
|
||||
# created on access if the object is persistent or if
|
||||
# col attributes have non-None. This would be better
|
||||
# if the composite were created unconditionally,
|
||||
# but that would be a behavioral change.
|
||||
if self.key not in dict_ and (
|
||||
state.key is not None or
|
||||
state.key is not None or
|
||||
not _none_set.issuperset(values)
|
||||
):
|
||||
dict_[self.key] = self.composite_class(*values)
|
||||
@@ -139,7 +139,7 @@ class CompositeProperty(DescriptorProperty):
|
||||
setattr(instance, key, None)
|
||||
else:
|
||||
for key, value in zip(
|
||||
self._attribute_keys,
|
||||
self._attribute_keys,
|
||||
value.__composite_values__()):
|
||||
setattr(instance, key, value)
|
||||
|
||||
@@ -198,7 +198,7 @@ class CompositeProperty(DescriptorProperty):
|
||||
return
|
||||
|
||||
# if column elements aren't loaded, skip.
|
||||
# __get__() will initiate a load for those
|
||||
# __get__() will initiate a load for those
|
||||
# columns
|
||||
for k in self._attribute_keys:
|
||||
if k not in dict_:
|
||||
@@ -206,7 +206,7 @@ class CompositeProperty(DescriptorProperty):
|
||||
|
||||
#assert self.key not in dict_
|
||||
dict_[self.key] = self.composite_class(
|
||||
*[state.dict[key] for key in
|
||||
*[state.dict[key] for key in
|
||||
self._attribute_keys]
|
||||
)
|
||||
|
||||
@@ -217,16 +217,16 @@ class CompositeProperty(DescriptorProperty):
|
||||
def insert_update_handler(mapper, connection, state):
|
||||
"""After an insert or update, some columns may be expired due
|
||||
to server side defaults, or re-populated due to client side
|
||||
defaults. Pop out the composite value here so that it
|
||||
defaults. Pop out the composite value here so that it
|
||||
recreates.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
state.dict.pop(self.key, None)
|
||||
|
||||
event.listen(self.parent, 'after_insert',
|
||||
event.listen(self.parent, 'after_insert',
|
||||
insert_update_handler, raw=True)
|
||||
event.listen(self.parent, 'after_update',
|
||||
event.listen(self.parent, 'after_update',
|
||||
insert_update_handler, raw=True)
|
||||
event.listen(self.parent, 'load', load_handler, raw=True, propagate=True)
|
||||
event.listen(self.parent, 'refresh', load_handler, raw=True, propagate=True)
|
||||
@@ -307,19 +307,19 @@ class CompositeProperty(DescriptorProperty):
|
||||
return str(self.parent.class_.__name__) + "." + self.key
|
||||
|
||||
class ConcreteInheritedProperty(DescriptorProperty):
|
||||
"""A 'do nothing' :class:`.MapperProperty` that disables
|
||||
"""A 'do nothing' :class:`.MapperProperty` that disables
|
||||
an attribute on a concrete subclass that is only present
|
||||
on the inherited mapper, not the concrete classes' mapper.
|
||||
|
||||
Cases where this occurs include:
|
||||
|
||||
* When the superclass mapper is mapped against a
|
||||
"polymorphic union", which includes all attributes from
|
||||
* When the superclass mapper is mapped against a
|
||||
"polymorphic union", which includes all attributes from
|
||||
all subclasses.
|
||||
* When a relationship() is configured on an inherited mapper,
|
||||
but not on the subclass mapper. Concrete mappers require
|
||||
that relationship() is configured explicitly on each
|
||||
subclass.
|
||||
that relationship() is configured explicitly on each
|
||||
subclass.
|
||||
|
||||
"""
|
||||
|
||||
@@ -337,7 +337,7 @@ class ConcreteInheritedProperty(DescriptorProperty):
|
||||
def warn():
|
||||
raise AttributeError("Concrete %s does not implement "
|
||||
"attribute %r at the instance level. Add this "
|
||||
"property explicitly to %s." %
|
||||
"property explicitly to %s." %
|
||||
(self.parent, self.key, self.parent))
|
||||
|
||||
class NoninheritedConcreteProp(object):
|
||||
@@ -354,7 +354,7 @@ class ConcreteInheritedProperty(DescriptorProperty):
|
||||
|
||||
class SynonymProperty(DescriptorProperty):
|
||||
|
||||
def __init__(self, name, map_column=None,
|
||||
def __init__(self, name, map_column=None,
|
||||
descriptor=None, comparator_factory=None,
|
||||
doc=None):
|
||||
self.name = name
|
||||
@@ -387,7 +387,7 @@ class SynonymProperty(DescriptorProperty):
|
||||
if self.key not in parent.mapped_table.c:
|
||||
raise sa_exc.ArgumentError(
|
||||
"Can't compile synonym '%s': no column on table "
|
||||
"'%s' named '%s'"
|
||||
"'%s' named '%s'"
|
||||
% (self.name, parent.mapped_table.description, self.key))
|
||||
elif parent.mapped_table.c[self.key] in \
|
||||
parent._columntoproperty and \
|
||||
@@ -397,13 +397,13 @@ class SynonymProperty(DescriptorProperty):
|
||||
raise sa_exc.ArgumentError(
|
||||
"Can't call map_column=True for synonym %r=%r, "
|
||||
"a ColumnProperty already exists keyed to the name "
|
||||
"%r for column %r" %
|
||||
"%r for column %r" %
|
||||
(self.key, self.name, self.name, self.key)
|
||||
)
|
||||
p = properties.ColumnProperty(parent.mapped_table.c[self.key])
|
||||
parent._configure_property(
|
||||
self.name, p,
|
||||
init=init,
|
||||
self.name, p,
|
||||
init=init,
|
||||
setparent=True)
|
||||
p._mapped_by_synonym = self.key
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# orm/dynamic.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -12,7 +12,6 @@ basic add/delete mutation.
|
||||
"""
|
||||
|
||||
from sqlalchemy import log, util
|
||||
from sqlalchemy import exc as sa_exc
|
||||
from sqlalchemy.orm import exc as orm_exc
|
||||
from sqlalchemy.sql import operators
|
||||
from sqlalchemy.orm import (
|
||||
@@ -20,12 +19,16 @@ from sqlalchemy.orm import (
|
||||
)
|
||||
from sqlalchemy.orm.query import Query
|
||||
from sqlalchemy.orm.util import has_identity
|
||||
from sqlalchemy.orm import attributes, collections
|
||||
from sqlalchemy.orm import collections
|
||||
|
||||
class DynaLoader(strategies.AbstractRelationshipLoader):
|
||||
def init_class_attribute(self, mapper):
|
||||
self.is_class_level = True
|
||||
|
||||
if not self.uselist:
|
||||
util.warn(
|
||||
"On relationship %s, 'dynamic' loaders cannot be used with "
|
||||
"many-to-one/one-to-one relationships and/or "
|
||||
"uselist=False." % self.parent_property)
|
||||
strategies._register_attribute(self,
|
||||
mapper,
|
||||
useobject=True,
|
||||
@@ -63,7 +66,7 @@ class DynamicAttributeImpl(attributes.AttributeImpl):
|
||||
else:
|
||||
return self.query_class(self, state)
|
||||
|
||||
def get_collection(self, state, dict_, user_data=None,
|
||||
def get_collection(self, state, dict_, user_data=None,
|
||||
passive=attributes.PASSIVE_NO_INITIALIZE):
|
||||
if passive is not attributes.PASSIVE_OFF:
|
||||
return self._get_collection_history(state,
|
||||
@@ -97,7 +100,7 @@ class DynamicAttributeImpl(attributes.AttributeImpl):
|
||||
if self.key not in state.committed_state:
|
||||
state.committed_state[self.key] = CollectionHistory(self, state)
|
||||
|
||||
state.modified_event(dict_,
|
||||
state.modified_event(dict_,
|
||||
self,
|
||||
attributes.NEVER_SET)
|
||||
|
||||
@@ -107,7 +110,7 @@ class DynamicAttributeImpl(attributes.AttributeImpl):
|
||||
return state.committed_state[self.key]
|
||||
|
||||
def set(self, state, dict_, value, initiator,
|
||||
passive=attributes.PASSIVE_OFF,
|
||||
passive=attributes.PASSIVE_OFF,
|
||||
check_old=None, pop=False):
|
||||
if initiator and initiator.parent_token is self.parent_token:
|
||||
return
|
||||
@@ -144,8 +147,8 @@ class DynamicAttributeImpl(attributes.AttributeImpl):
|
||||
def get_all_pending(self, state, dict_):
|
||||
c = self._get_collection_history(state, True)
|
||||
return [
|
||||
(attributes.instance_state(x), x)
|
||||
for x in
|
||||
(attributes.instance_state(x), x)
|
||||
for x in
|
||||
c.added_items + c.unchanged_items + c.deleted_items
|
||||
]
|
||||
|
||||
@@ -160,12 +163,12 @@ class DynamicAttributeImpl(attributes.AttributeImpl):
|
||||
else:
|
||||
return c
|
||||
|
||||
def append(self, state, dict_, value, initiator,
|
||||
def append(self, state, dict_, value, initiator,
|
||||
passive=attributes.PASSIVE_OFF):
|
||||
if initiator is not self:
|
||||
self.fire_append_event(state, dict_, value, initiator)
|
||||
|
||||
def remove(self, state, dict_, value, initiator,
|
||||
def remove(self, state, dict_, value, initiator,
|
||||
passive=attributes.PASSIVE_OFF):
|
||||
if initiator is not self:
|
||||
self.fire_remove_event(state, dict_, value, initiator)
|
||||
@@ -204,9 +207,9 @@ class AppenderMixin(object):
|
||||
mapper = object_mapper(instance)
|
||||
prop = mapper._props[self.attr.key]
|
||||
self._criterion = prop.compare(
|
||||
operators.eq,
|
||||
instance,
|
||||
value_is_parent=True,
|
||||
operators.eq,
|
||||
instance,
|
||||
value_is_parent=True,
|
||||
alias_secondary=False)
|
||||
|
||||
if self.attr.order_by:
|
||||
@@ -280,12 +283,12 @@ class AppenderMixin(object):
|
||||
|
||||
def append(self, item):
|
||||
self.attr.append(
|
||||
attributes.instance_state(self.instance),
|
||||
attributes.instance_state(self.instance),
|
||||
attributes.instance_dict(self.instance), item, None)
|
||||
|
||||
def remove(self, item):
|
||||
self.attr.remove(
|
||||
attributes.instance_state(self.instance),
|
||||
attributes.instance_state(self.instance),
|
||||
attributes.instance_dict(self.instance), item, None)
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# orm/evaluator.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -13,10 +13,10 @@ class UnevaluatableError(Exception):
|
||||
pass
|
||||
|
||||
_straight_ops = set(getattr(operators, op)
|
||||
for op in ('add', 'mul', 'sub',
|
||||
for op in ('add', 'mul', 'sub',
|
||||
# Py2K
|
||||
'div',
|
||||
# end Py2K
|
||||
# end Py2K
|
||||
'mod', 'truediv',
|
||||
'lt', 'le', 'ne', 'gt', 'ge', 'eq'))
|
||||
|
||||
|
||||
+175
-167
@@ -1,5 +1,5 @@
|
||||
# orm/events.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -91,11 +91,11 @@ class InstanceEvents(event.Events):
|
||||
When using :class:`.InstanceEvents`, several modifiers are
|
||||
available to the :func:`.event.listen` function.
|
||||
|
||||
:param propagate=False: When True, the event listener should
|
||||
be applied to all inheriting mappers as well as the
|
||||
:param propagate=False: When True, the event listener should
|
||||
be applied to all inheriting mappers as well as the
|
||||
mapper which is the target of this listener.
|
||||
:param raw=False: When True, the "target" argument passed
|
||||
to applicable event listener functions will be the
|
||||
to applicable event listener functions will be the
|
||||
instance's :class:`.InstanceState` management
|
||||
object, rather than the mapped instance itself.
|
||||
|
||||
@@ -142,17 +142,17 @@ class InstanceEvents(event.Events):
|
||||
def init(self, target, args, kwargs):
|
||||
"""Receive an instance when it's constructor is called.
|
||||
|
||||
This method is only called during a userland construction of
|
||||
This method is only called during a userland construction of
|
||||
an object. It is not called when an object is loaded from the
|
||||
database.
|
||||
|
||||
"""
|
||||
|
||||
def init_failure(self, target, args, kwargs):
|
||||
"""Receive an instance when it's constructor has been called,
|
||||
"""Receive an instance when it's constructor has been called,
|
||||
and raised an exception.
|
||||
|
||||
This method is only called during a userland construction of
|
||||
This method is only called during a userland construction of
|
||||
an object. It is not called when an object is loaded from the
|
||||
database.
|
||||
|
||||
@@ -168,12 +168,12 @@ class InstanceEvents(event.Events):
|
||||
instance's lifetime.
|
||||
|
||||
Note that during a result-row load, this method is called upon
|
||||
the first row received for this instance. Note that some
|
||||
attributes and collections may or may not be loaded or even
|
||||
the first row received for this instance. Note that some
|
||||
attributes and collections may or may not be loaded or even
|
||||
initialized, depending on what's present in the result rows.
|
||||
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
:param context: the :class:`.QueryContext` corresponding to the
|
||||
@@ -184,16 +184,16 @@ class InstanceEvents(event.Events):
|
||||
"""
|
||||
|
||||
def refresh(self, target, context, attrs):
|
||||
"""Receive an object instance after one or more attributes have
|
||||
"""Receive an object instance after one or more attributes have
|
||||
been refreshed from a query.
|
||||
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
:param context: the :class:`.QueryContext` corresponding to the
|
||||
current :class:`.Query` in progress.
|
||||
:param attrs: iterable collection of attribute names which
|
||||
:param attrs: iterable collection of attribute names which
|
||||
were populated, or None if all column-mapped, non-deferred
|
||||
attributes were populated.
|
||||
|
||||
@@ -206,23 +206,23 @@ class InstanceEvents(event.Events):
|
||||
'keys' is a list of attribute names. If None, the entire
|
||||
state was expired.
|
||||
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
:param attrs: iterable collection of attribute
|
||||
names which were expired, or None if all attributes were
|
||||
names which were expired, or None if all attributes were
|
||||
expired.
|
||||
|
||||
"""
|
||||
|
||||
def resurrect(self, target):
|
||||
"""Receive an object instance as it is 'resurrected' from
|
||||
"""Receive an object instance as it is 'resurrected' from
|
||||
garbage collection, which occurs when a "dirty" state falls
|
||||
out of scope.
|
||||
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
|
||||
@@ -232,28 +232,28 @@ class InstanceEvents(event.Events):
|
||||
"""Receive an object instance when its associated state is
|
||||
being pickled.
|
||||
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
:param state_dict: the dictionary returned by
|
||||
:param state_dict: the dictionary returned by
|
||||
:class:`.InstanceState.__getstate__`, containing the state
|
||||
to be pickled.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def unpickle(self, target, state_dict):
|
||||
"""Receive an object instance after it's associated state has
|
||||
been unpickled.
|
||||
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
:param state_dict: the dictionary sent to
|
||||
:class:`.InstanceState.__setstate__`, containing the state
|
||||
dictionary which was pickled.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
class MapperEvents(event.Events):
|
||||
@@ -267,7 +267,7 @@ class MapperEvents(event.Events):
|
||||
# execute a stored procedure upon INSERT,
|
||||
# apply the value to the row to be inserted
|
||||
target.calculated_value = connection.scalar(
|
||||
"select my_special_function(%d)"
|
||||
"select my_special_function(%d)"
|
||||
% target.special_number)
|
||||
|
||||
# associate the listener function with SomeMappedClass,
|
||||
@@ -304,16 +304,16 @@ class MapperEvents(event.Events):
|
||||
When using :class:`.MapperEvents`, several modifiers are
|
||||
available to the :func:`.event.listen` function.
|
||||
|
||||
:param propagate=False: When True, the event listener should
|
||||
be applied to all inheriting mappers as well as the
|
||||
:param propagate=False: When True, the event listener should
|
||||
be applied to all inheriting mappers as well as the
|
||||
mapper which is the target of this listener.
|
||||
:param raw=False: When True, the "target" argument passed
|
||||
to applicable event listener functions will be the
|
||||
to applicable event listener functions will be the
|
||||
instance's :class:`.InstanceState` management
|
||||
object, rather than the mapped instance itself.
|
||||
:param retval=False: when True, the user-defined event function
|
||||
must have a return value, the purpose of which is either to
|
||||
control subsequent event propagation, or to otherwise alter
|
||||
control subsequent event propagation, or to otherwise alter
|
||||
the operation in progress by the mapper. Possible return
|
||||
values are:
|
||||
|
||||
@@ -322,7 +322,7 @@ class MapperEvents(event.Events):
|
||||
* ``sqlalchemy.orm.interfaces.EXT_STOP`` - cancel all subsequent
|
||||
event handlers in the chain.
|
||||
* other values - the return value specified by specific listeners,
|
||||
such as :meth:`~.MapperEvents.translate_row` or
|
||||
such as :meth:`~.MapperEvents.translate_row` or
|
||||
:meth:`~.MapperEvents.create_instance`.
|
||||
|
||||
"""
|
||||
@@ -340,7 +340,7 @@ class MapperEvents(event.Events):
|
||||
return target
|
||||
|
||||
@classmethod
|
||||
def _listen(cls, target, identifier, fn,
|
||||
def _listen(cls, target, identifier, fn,
|
||||
raw=False, retval=False, propagate=False):
|
||||
|
||||
if not raw or not retval:
|
||||
@@ -370,7 +370,7 @@ class MapperEvents(event.Events):
|
||||
event.Events._listen(target, identifier, fn)
|
||||
|
||||
def instrument_class(self, mapper, class_):
|
||||
"""Receive a class when the mapper is first constructed,
|
||||
"""Receive a class when the mapper is first constructed,
|
||||
before instrumentation is applied to the mapped class.
|
||||
|
||||
This event is the earliest phase of mapper construction.
|
||||
@@ -388,8 +388,16 @@ class MapperEvents(event.Events):
|
||||
def mapper_configured(self, mapper, class_):
|
||||
"""Called when the mapper for the class is fully configured.
|
||||
|
||||
This event is the latest phase of mapper construction.
|
||||
The mapper should be in its final state.
|
||||
This event is the latest phase of mapper construction, and
|
||||
is invoked when the mapped classes are first used, so that relationships
|
||||
between mappers can be resolved. When the event is called,
|
||||
the mapper should be in its final state.
|
||||
|
||||
While the configuration event normally occurs automatically,
|
||||
it can be forced to occur ahead of time, in the case where the event
|
||||
is needed before any actual mapper usage, by using the
|
||||
:func:`.configure_mappers` function.
|
||||
|
||||
|
||||
:param mapper: the :class:`.Mapper` which is the target
|
||||
of this event.
|
||||
@@ -404,11 +412,11 @@ class MapperEvents(event.Events):
|
||||
This corresponds to the :func:`.orm.configure_mappers` call, which
|
||||
note is usually called automatically as mappings are first
|
||||
used.
|
||||
|
||||
|
||||
Theoretically this event is called once per
|
||||
application, but is actually called any time new mappers
|
||||
have been affected by a :func:`.orm.configure_mappers` call. If new mappings
|
||||
are constructed after existing ones have already been used,
|
||||
are constructed after existing ones have already been used,
|
||||
this event can be called again.
|
||||
|
||||
"""
|
||||
@@ -420,9 +428,9 @@ class MapperEvents(event.Events):
|
||||
This listener is typically registered with ``retval=True``.
|
||||
It is called when the mapper first receives a row, before
|
||||
the object identity or the instance itself has been derived
|
||||
from that row. The given row may or may not be a
|
||||
from that row. The given row may or may not be a
|
||||
:class:`.RowProxy` object - it will always be a dictionary-like
|
||||
object which contains mapped columns as keys. The
|
||||
object which contains mapped columns as keys. The
|
||||
returned object should also be a dictionary-like object
|
||||
which recognizes mapped columns as keys.
|
||||
|
||||
@@ -431,7 +439,7 @@ class MapperEvents(event.Events):
|
||||
:param context: the :class:`.QueryContext`, which includes
|
||||
a handle to the current :class:`.Query` in progress as well
|
||||
as additional state information.
|
||||
:param row: the result row being handled. This may be
|
||||
:param row: the result row being handled. This may be
|
||||
an actual :class:`.RowProxy` or may be a dictionary containing
|
||||
:class:`.Column` objects as keys.
|
||||
:return: When configured with ``retval=True``, the function
|
||||
@@ -454,18 +462,18 @@ class MapperEvents(event.Events):
|
||||
:param context: the :class:`.QueryContext`, which includes
|
||||
a handle to the current :class:`.Query` in progress as well
|
||||
as additional state information.
|
||||
:param row: the result row being handled. This may be
|
||||
:param row: the result row being handled. This may be
|
||||
an actual :class:`.RowProxy` or may be a dictionary containing
|
||||
:class:`.Column` objects as keys.
|
||||
:param class\_: the mapped class.
|
||||
:return: When configured with ``retval=True``, the return value
|
||||
should be a newly created instance of the mapped class,
|
||||
should be a newly created instance of the mapped class,
|
||||
or ``EXT_CONTINUE`` indicating that default object construction
|
||||
should take place.
|
||||
|
||||
"""
|
||||
|
||||
def append_result(self, mapper, context, row, target,
|
||||
def append_result(self, mapper, context, row, target,
|
||||
result, **flags):
|
||||
"""Receive an object instance before that instance is appended
|
||||
to a result list.
|
||||
@@ -478,27 +486,27 @@ class MapperEvents(event.Events):
|
||||
:param context: the :class:`.QueryContext`, which includes
|
||||
a handle to the current :class:`.Query` in progress as well
|
||||
as additional state information.
|
||||
:param row: the result row being handled. This may be
|
||||
:param row: the result row being handled. This may be
|
||||
an actual :class:`.RowProxy` or may be a dictionary containing
|
||||
:class:`.Column` objects as keys.
|
||||
:param target: the mapped instance being populated. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance being populated. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
:param result: a list-like object where results are being
|
||||
appended.
|
||||
:param \**flags: Additional state information about the
|
||||
:param \**flags: Additional state information about the
|
||||
current handling of the row.
|
||||
:return: If this method is registered with ``retval=True``,
|
||||
a return value of ``EXT_STOP`` will prevent the instance
|
||||
from being appended to the given result list, whereas a
|
||||
from being appended to the given result list, whereas a
|
||||
return value of ``EXT_CONTINUE`` will result in the default
|
||||
behavior of appending the value to the result list.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def populate_instance(self, mapper, context, row,
|
||||
def populate_instance(self, mapper, context, row,
|
||||
target, **flags):
|
||||
"""Receive an instance before that instance has
|
||||
its attributes populated.
|
||||
@@ -518,11 +526,11 @@ class MapperEvents(event.Events):
|
||||
:param context: the :class:`.QueryContext`, which includes
|
||||
a handle to the current :class:`.Query` in progress as well
|
||||
as additional state information.
|
||||
:param row: the result row being handled. This may be
|
||||
:param row: the result row being handled. This may be
|
||||
an actual :class:`.RowProxy` or may be a dictionary containing
|
||||
:class:`.Column` objects as keys.
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
:return: When configured with ``retval=True``, a return
|
||||
@@ -536,9 +544,9 @@ class MapperEvents(event.Events):
|
||||
"""Receive an object instance before an INSERT statement
|
||||
is emitted corresponding to that instance.
|
||||
|
||||
This event is used to modify local, non-object related
|
||||
This event is used to modify local, non-object related
|
||||
attributes on the instance before an INSERT occurs, as well
|
||||
as to emit additional SQL statements on the given
|
||||
as to emit additional SQL statements on the given
|
||||
connection.
|
||||
|
||||
The event is often called for a batch of objects of the
|
||||
@@ -552,23 +560,23 @@ class MapperEvents(event.Events):
|
||||
|
||||
.. warning::
|
||||
Mapper-level flush events are designed to operate **on attributes
|
||||
local to the immediate object being handled
|
||||
local to the immediate object being handled
|
||||
and via SQL operations with the given** :class:`.Connection` **only.**
|
||||
Handlers here should **not** make alterations to the state of
|
||||
Handlers here should **not** make alterations to the state of
|
||||
the :class:`.Session` overall, and in general should not
|
||||
affect any :func:`.relationship` -mapped attributes, as
|
||||
affect any :func:`.relationship` -mapped attributes, as
|
||||
session cascade rules will not function properly, nor is it
|
||||
always known if the related class has already been handled.
|
||||
always known if the related class has already been handled.
|
||||
Operations that **are not supported in mapper events** include:
|
||||
|
||||
|
||||
* :meth:`.Session.add`
|
||||
* :meth:`.Session.delete`
|
||||
* Mapped collection append, add, remove, delete, discard, etc.
|
||||
* Mapped relationship attribute set/del events, i.e. ``someobject.related = someotherobject``
|
||||
|
||||
|
||||
Operations which manipulate the state of the object
|
||||
relative to other objects are better handled:
|
||||
|
||||
|
||||
* In the ``__init__()`` method of the mapped object itself, or another method
|
||||
designed to establish some particular state.
|
||||
* In a ``@validates`` handler, see :ref:`simple_validators`
|
||||
@@ -576,12 +584,12 @@ class MapperEvents(event.Events):
|
||||
|
||||
:param mapper: the :class:`.Mapper` which is the target
|
||||
of this event.
|
||||
:param connection: the :class:`.Connection` being used to
|
||||
:param connection: the :class:`.Connection` being used to
|
||||
emit INSERT statements for this instance. This
|
||||
provides a handle into the current transaction on the
|
||||
provides a handle into the current transaction on the
|
||||
target database specific to this instance.
|
||||
:param target: the mapped instance being persisted. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance being persisted. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
:return: No return value is supported by this event.
|
||||
@@ -594,7 +602,7 @@ class MapperEvents(event.Events):
|
||||
|
||||
This event is used to modify in-Python-only
|
||||
state on the instance after an INSERT occurs, as well
|
||||
as to emit additional SQL statements on the given
|
||||
as to emit additional SQL statements on the given
|
||||
connection.
|
||||
|
||||
The event is often called for a batch of objects of the
|
||||
@@ -608,23 +616,23 @@ class MapperEvents(event.Events):
|
||||
|
||||
.. warning::
|
||||
Mapper-level flush events are designed to operate **on attributes
|
||||
local to the immediate object being handled
|
||||
local to the immediate object being handled
|
||||
and via SQL operations with the given** :class:`.Connection` **only.**
|
||||
Handlers here should **not** make alterations to the state of
|
||||
Handlers here should **not** make alterations to the state of
|
||||
the :class:`.Session` overall, and in general should not
|
||||
affect any :func:`.relationship` -mapped attributes, as
|
||||
affect any :func:`.relationship` -mapped attributes, as
|
||||
session cascade rules will not function properly, nor is it
|
||||
always known if the related class has already been handled.
|
||||
always known if the related class has already been handled.
|
||||
Operations that **are not supported in mapper events** include:
|
||||
|
||||
|
||||
* :meth:`.Session.add`
|
||||
* :meth:`.Session.delete`
|
||||
* Mapped collection append, add, remove, delete, discard, etc.
|
||||
* Mapped relationship attribute set/del events, i.e. ``someobject.related = someotherobject``
|
||||
|
||||
|
||||
Operations which manipulate the state of the object
|
||||
relative to other objects are better handled:
|
||||
|
||||
|
||||
* In the ``__init__()`` method of the mapped object itself, or another method
|
||||
designed to establish some particular state.
|
||||
* In a ``@validates`` handler, see :ref:`simple_validators`
|
||||
@@ -632,12 +640,12 @@ class MapperEvents(event.Events):
|
||||
|
||||
:param mapper: the :class:`.Mapper` which is the target
|
||||
of this event.
|
||||
:param connection: the :class:`.Connection` being used to
|
||||
:param connection: the :class:`.Connection` being used to
|
||||
emit INSERT statements for this instance. This
|
||||
provides a handle into the current transaction on the
|
||||
provides a handle into the current transaction on the
|
||||
target database specific to this instance.
|
||||
:param target: the mapped instance being persisted. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance being persisted. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
:return: No return value is supported by this event.
|
||||
@@ -648,9 +656,9 @@ class MapperEvents(event.Events):
|
||||
"""Receive an object instance before an UPDATE statement
|
||||
is emitted corresponding to that instance.
|
||||
|
||||
This event is used to modify local, non-object related
|
||||
This event is used to modify local, non-object related
|
||||
attributes on the instance before an UPDATE occurs, as well
|
||||
as to emit additional SQL statements on the given
|
||||
as to emit additional SQL statements on the given
|
||||
connection.
|
||||
|
||||
This method is called for all instances that are
|
||||
@@ -683,23 +691,23 @@ class MapperEvents(event.Events):
|
||||
|
||||
.. warning::
|
||||
Mapper-level flush events are designed to operate **on attributes
|
||||
local to the immediate object being handled
|
||||
local to the immediate object being handled
|
||||
and via SQL operations with the given** :class:`.Connection` **only.**
|
||||
Handlers here should **not** make alterations to the state of
|
||||
Handlers here should **not** make alterations to the state of
|
||||
the :class:`.Session` overall, and in general should not
|
||||
affect any :func:`.relationship` -mapped attributes, as
|
||||
affect any :func:`.relationship` -mapped attributes, as
|
||||
session cascade rules will not function properly, nor is it
|
||||
always known if the related class has already been handled.
|
||||
always known if the related class has already been handled.
|
||||
Operations that **are not supported in mapper events** include:
|
||||
|
||||
|
||||
* :meth:`.Session.add`
|
||||
* :meth:`.Session.delete`
|
||||
* Mapped collection append, add, remove, delete, discard, etc.
|
||||
* Mapped relationship attribute set/del events, i.e. ``someobject.related = someotherobject``
|
||||
|
||||
|
||||
Operations which manipulate the state of the object
|
||||
relative to other objects are better handled:
|
||||
|
||||
|
||||
* In the ``__init__()`` method of the mapped object itself, or another method
|
||||
designed to establish some particular state.
|
||||
* In a ``@validates`` handler, see :ref:`simple_validators`
|
||||
@@ -707,12 +715,12 @@ class MapperEvents(event.Events):
|
||||
|
||||
:param mapper: the :class:`.Mapper` which is the target
|
||||
of this event.
|
||||
:param connection: the :class:`.Connection` being used to
|
||||
:param connection: the :class:`.Connection` being used to
|
||||
emit UPDATE statements for this instance. This
|
||||
provides a handle into the current transaction on the
|
||||
provides a handle into the current transaction on the
|
||||
target database specific to this instance.
|
||||
:param target: the mapped instance being persisted. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance being persisted. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
:return: No return value is supported by this event.
|
||||
@@ -724,12 +732,12 @@ class MapperEvents(event.Events):
|
||||
|
||||
This event is used to modify in-Python-only
|
||||
state on the instance after an UPDATE occurs, as well
|
||||
as to emit additional SQL statements on the given
|
||||
as to emit additional SQL statements on the given
|
||||
connection.
|
||||
|
||||
This method is called for all instances that are
|
||||
marked as "dirty", *even those which have no net changes
|
||||
to their column-based attributes*, and for which
|
||||
to their column-based attributes*, and for which
|
||||
no UPDATE statement has proceeded. An object is marked
|
||||
as dirty when any of its column-based attributes have a
|
||||
"set attribute" operation called or when any of its
|
||||
@@ -756,23 +764,23 @@ class MapperEvents(event.Events):
|
||||
|
||||
.. warning::
|
||||
Mapper-level flush events are designed to operate **on attributes
|
||||
local to the immediate object being handled
|
||||
local to the immediate object being handled
|
||||
and via SQL operations with the given** :class:`.Connection` **only.**
|
||||
Handlers here should **not** make alterations to the state of
|
||||
Handlers here should **not** make alterations to the state of
|
||||
the :class:`.Session` overall, and in general should not
|
||||
affect any :func:`.relationship` -mapped attributes, as
|
||||
affect any :func:`.relationship` -mapped attributes, as
|
||||
session cascade rules will not function properly, nor is it
|
||||
always known if the related class has already been handled.
|
||||
always known if the related class has already been handled.
|
||||
Operations that **are not supported in mapper events** include:
|
||||
|
||||
|
||||
* :meth:`.Session.add`
|
||||
* :meth:`.Session.delete`
|
||||
* Mapped collection append, add, remove, delete, discard, etc.
|
||||
* Mapped relationship attribute set/del events, i.e. ``someobject.related = someotherobject``
|
||||
|
||||
|
||||
Operations which manipulate the state of the object
|
||||
relative to other objects are better handled:
|
||||
|
||||
|
||||
* In the ``__init__()`` method of the mapped object itself, or another method
|
||||
designed to establish some particular state.
|
||||
* In a ``@validates`` handler, see :ref:`simple_validators`
|
||||
@@ -780,12 +788,12 @@ class MapperEvents(event.Events):
|
||||
|
||||
:param mapper: the :class:`.Mapper` which is the target
|
||||
of this event.
|
||||
:param connection: the :class:`.Connection` being used to
|
||||
:param connection: the :class:`.Connection` being used to
|
||||
emit UPDATE statements for this instance. This
|
||||
provides a handle into the current transaction on the
|
||||
provides a handle into the current transaction on the
|
||||
target database specific to this instance.
|
||||
:param target: the mapped instance being persisted. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance being persisted. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
:return: No return value is supported by this event.
|
||||
@@ -796,33 +804,33 @@ class MapperEvents(event.Events):
|
||||
"""Receive an object instance before a DELETE statement
|
||||
is emitted corresponding to that instance.
|
||||
|
||||
This event is used to emit additional SQL statements on
|
||||
This event is used to emit additional SQL statements on
|
||||
the given connection as well as to perform application
|
||||
specific bookkeeping related to a deletion event.
|
||||
|
||||
The event is often called for a batch of objects of the
|
||||
same class before their DELETE statements are emitted at
|
||||
once in a later step.
|
||||
once in a later step.
|
||||
|
||||
.. warning::
|
||||
Mapper-level flush events are designed to operate **on attributes
|
||||
local to the immediate object being handled
|
||||
local to the immediate object being handled
|
||||
and via SQL operations with the given** :class:`.Connection` **only.**
|
||||
Handlers here should **not** make alterations to the state of
|
||||
Handlers here should **not** make alterations to the state of
|
||||
the :class:`.Session` overall, and in general should not
|
||||
affect any :func:`.relationship` -mapped attributes, as
|
||||
affect any :func:`.relationship` -mapped attributes, as
|
||||
session cascade rules will not function properly, nor is it
|
||||
always known if the related class has already been handled.
|
||||
always known if the related class has already been handled.
|
||||
Operations that **are not supported in mapper events** include:
|
||||
|
||||
|
||||
* :meth:`.Session.add`
|
||||
* :meth:`.Session.delete`
|
||||
* Mapped collection append, add, remove, delete, discard, etc.
|
||||
* Mapped relationship attribute set/del events, i.e. ``someobject.related = someotherobject``
|
||||
|
||||
|
||||
Operations which manipulate the state of the object
|
||||
relative to other objects are better handled:
|
||||
|
||||
|
||||
* In the ``__init__()`` method of the mapped object itself, or another method
|
||||
designed to establish some particular state.
|
||||
* In a ``@validates`` handler, see :ref:`simple_validators`
|
||||
@@ -830,12 +838,12 @@ class MapperEvents(event.Events):
|
||||
|
||||
:param mapper: the :class:`.Mapper` which is the target
|
||||
of this event.
|
||||
:param connection: the :class:`.Connection` being used to
|
||||
:param connection: the :class:`.Connection` being used to
|
||||
emit DELETE statements for this instance. This
|
||||
provides a handle into the current transaction on the
|
||||
provides a handle into the current transaction on the
|
||||
target database specific to this instance.
|
||||
:param target: the mapped instance being deleted. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance being deleted. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
:return: No return value is supported by this event.
|
||||
@@ -846,33 +854,33 @@ class MapperEvents(event.Events):
|
||||
"""Receive an object instance after a DELETE statement
|
||||
has been emitted corresponding to that instance.
|
||||
|
||||
This event is used to emit additional SQL statements on
|
||||
This event is used to emit additional SQL statements on
|
||||
the given connection as well as to perform application
|
||||
specific bookkeeping related to a deletion event.
|
||||
|
||||
The event is often called for a batch of objects of the
|
||||
same class after their DELETE statements have been emitted at
|
||||
once in a previous step.
|
||||
once in a previous step.
|
||||
|
||||
.. warning::
|
||||
Mapper-level flush events are designed to operate **on attributes
|
||||
local to the immediate object being handled
|
||||
local to the immediate object being handled
|
||||
and via SQL operations with the given** :class:`.Connection` **only.**
|
||||
Handlers here should **not** make alterations to the state of
|
||||
Handlers here should **not** make alterations to the state of
|
||||
the :class:`.Session` overall, and in general should not
|
||||
affect any :func:`.relationship` -mapped attributes, as
|
||||
affect any :func:`.relationship` -mapped attributes, as
|
||||
session cascade rules will not function properly, nor is it
|
||||
always known if the related class has already been handled.
|
||||
always known if the related class has already been handled.
|
||||
Operations that **are not supported in mapper events** include:
|
||||
|
||||
|
||||
* :meth:`.Session.add`
|
||||
* :meth:`.Session.delete`
|
||||
* Mapped collection append, add, remove, delete, discard, etc.
|
||||
* Mapped relationship attribute set/del events, i.e. ``someobject.related = someotherobject``
|
||||
|
||||
|
||||
Operations which manipulate the state of the object
|
||||
relative to other objects are better handled:
|
||||
|
||||
|
||||
* In the ``__init__()`` method of the mapped object itself, or another method
|
||||
designed to establish some particular state.
|
||||
* In a ``@validates`` handler, see :ref:`simple_validators`
|
||||
@@ -880,12 +888,12 @@ class MapperEvents(event.Events):
|
||||
|
||||
:param mapper: the :class:`.Mapper` which is the target
|
||||
of this event.
|
||||
:param connection: the :class:`.Connection` being used to
|
||||
:param connection: the :class:`.Connection` being used to
|
||||
emit DELETE statements for this instance. This
|
||||
provides a handle into the current transaction on the
|
||||
provides a handle into the current transaction on the
|
||||
target database specific to this instance.
|
||||
:param target: the mapped instance being deleted. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
:param target: the mapped instance being deleted. If
|
||||
the event is configured with ``raw=True``, this will
|
||||
instead be the :class:`.InstanceState` state-management
|
||||
object associated with the instance.
|
||||
:return: No return value is supported by this event.
|
||||
@@ -952,7 +960,7 @@ class SessionEvents(event.Events):
|
||||
transaction is ongoing.
|
||||
|
||||
:param session: The target :class:`.Session`.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def after_commit(self, session):
|
||||
@@ -960,19 +968,19 @@ class SessionEvents(event.Events):
|
||||
|
||||
Note that this may not be per-flush if a longer running
|
||||
transaction is ongoing.
|
||||
|
||||
|
||||
:param session: The target :class:`.Session`.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def after_rollback(self, session):
|
||||
"""Execute after a real DBAPI rollback has occurred.
|
||||
|
||||
|
||||
Note that this event only fires when the *actual* rollback against
|
||||
the database occurs - it does *not* fire each time the
|
||||
:meth:`.Session.rollback` method is called, if the underlying
|
||||
the database occurs - it does *not* fire each time the
|
||||
:meth:`.Session.rollback` method is called, if the underlying
|
||||
DBAPI transaction has already been rolled back. In many
|
||||
cases, the :class:`.Session` will not be in
|
||||
cases, the :class:`.Session` will not be in
|
||||
an "active" state during this event, as the current
|
||||
transaction is not valid. To acquire a :class:`.Session`
|
||||
which is active after the outermost rollback has proceeded,
|
||||
@@ -984,30 +992,30 @@ class SessionEvents(event.Events):
|
||||
"""
|
||||
|
||||
def after_soft_rollback(self, session, previous_transaction):
|
||||
"""Execute after any rollback has occurred, including "soft"
|
||||
"""Execute after any rollback has occurred, including "soft"
|
||||
rollbacks that don't actually emit at the DBAPI level.
|
||||
|
||||
|
||||
This corresponds to both nested and outer rollbacks, i.e.
|
||||
the innermost rollback that calls the DBAPI's
|
||||
rollback() method, as well as the enclosing rollback
|
||||
the innermost rollback that calls the DBAPI's
|
||||
rollback() method, as well as the enclosing rollback
|
||||
calls that only pop themselves from the transaction stack.
|
||||
|
||||
The given :class:`.Session` can be used to invoke SQL and
|
||||
:meth:`.Session.query` operations after an outermost rollback
|
||||
|
||||
The given :class:`.Session` can be used to invoke SQL and
|
||||
:meth:`.Session.query` operations after an outermost rollback
|
||||
by first checking the :attr:`.Session.is_active` flag::
|
||||
|
||||
@event.listens_for(Session, "after_soft_rollback")
|
||||
def do_something(session, previous_transaction):
|
||||
if session.is_active:
|
||||
session.execute("select * from some_table")
|
||||
|
||||
|
||||
:param session: The target :class:`.Session`.
|
||||
:param previous_transaction: The :class:`.SessionTransaction` transactional
|
||||
marker object which was just closed. The current :class:`.SessionTransaction`
|
||||
for the given :class:`.Session` is available via the
|
||||
:attr:`.Session.transaction` attribute.
|
||||
|
||||
New in 0.7.3.
|
||||
.. versionadded:: 0.7.3
|
||||
|
||||
"""
|
||||
|
||||
@@ -1030,7 +1038,7 @@ class SessionEvents(event.Events):
|
||||
Note that the session's state is still in pre-flush, i.e. 'new',
|
||||
'dirty', and 'deleted' lists still show pre-flush state as well
|
||||
as the history settings on instance attributes.
|
||||
|
||||
|
||||
:param session: The target :class:`.Session`.
|
||||
:param flush_context: Internal :class:`.UOWTransaction` object
|
||||
which handles the details of the flush.
|
||||
@@ -1044,8 +1052,8 @@ class SessionEvents(event.Events):
|
||||
This will be when the 'new', 'dirty', and 'deleted' lists are in
|
||||
their final state. An actual commit() may or may not have
|
||||
occurred, depending on whether or not the flush started its own
|
||||
transaction or participated in a larger transaction.
|
||||
|
||||
transaction or participated in a larger transaction.
|
||||
|
||||
:param session: The target :class:`.Session`.
|
||||
:param flush_context: Internal :class:`.UOWTransaction` object
|
||||
which handles the details of the flush.
|
||||
@@ -1056,9 +1064,9 @@ class SessionEvents(event.Events):
|
||||
|
||||
:param session: The target :class:`.Session`.
|
||||
:param transaction: The :class:`.SessionTransaction`.
|
||||
:param connection: The :class:`~.engine.base.Connection` object
|
||||
:param connection: The :class:`~.engine.base.Connection` object
|
||||
which will be used for SQL statements.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def after_attach(self, session, instance):
|
||||
@@ -1072,7 +1080,7 @@ class SessionEvents(event.Events):
|
||||
This is called as a result of the :meth:`.Query.update` method.
|
||||
|
||||
:param query: the :class:`.Query` object that this update operation was
|
||||
called upon.
|
||||
called upon.
|
||||
:param query_context: The :class:`.QueryContext` object, corresponding
|
||||
to the invocation of an ORM query.
|
||||
:param result: the :class:`.ResultProxy` returned as a result of the
|
||||
@@ -1086,7 +1094,7 @@ class SessionEvents(event.Events):
|
||||
This is called as a result of the :meth:`.Query.delete` method.
|
||||
|
||||
:param query: the :class:`.Query` object that this update operation was
|
||||
called upon.
|
||||
called upon.
|
||||
:param query_context: The :class:`.QueryContext` object, corresponding
|
||||
to the invocation of an ORM query.
|
||||
:param result: the :class:`.ResultProxy` returned as a result of the
|
||||
@@ -1137,15 +1145,15 @@ class AttributeEvents(event.Events):
|
||||
|
||||
:param propagate=False: When True, the listener function will
|
||||
be established not just for the class attribute given, but
|
||||
for attributes of the same name on all current subclasses
|
||||
of that class, as well as all future subclasses of that
|
||||
class, using an additional listener that listens for
|
||||
for attributes of the same name on all current subclasses
|
||||
of that class, as well as all future subclasses of that
|
||||
class, using an additional listener that listens for
|
||||
instrumentation events.
|
||||
:param raw=False: When True, the "target" argument to the
|
||||
event will be the :class:`.InstanceState` management
|
||||
object, rather than the mapped instance itself.
|
||||
:param retval=False: when True, the user-defined event
|
||||
listening must return the "value" argument from the
|
||||
:param retval=False: when True, the user-defined event
|
||||
listening must return the "value" argument from the
|
||||
function. This gives the listening function the opportunity
|
||||
to change the value that is ultimately used for a "set"
|
||||
or "append" event.
|
||||
@@ -1161,7 +1169,7 @@ class AttributeEvents(event.Events):
|
||||
return target
|
||||
|
||||
@classmethod
|
||||
def _listen(cls, target, identifier, fn, active_history=False,
|
||||
def _listen(cls, target, identifier, fn, active_history=False,
|
||||
raw=False, retval=False,
|
||||
propagate=False):
|
||||
if active_history:
|
||||
@@ -1202,9 +1210,9 @@ class AttributeEvents(event.Events):
|
||||
be the :class:`.InstanceState` object.
|
||||
:param value: the value being appended. If this listener
|
||||
is registered with ``retval=True``, the listener
|
||||
function must return this value, or a new value which
|
||||
function must return this value, or a new value which
|
||||
replaces it.
|
||||
:param initiator: the attribute implementation object
|
||||
:param initiator: the attribute implementation object
|
||||
which initiated this event.
|
||||
:return: if the event was registered with ``retval=True``,
|
||||
the given value, or a new effective value, should be returned.
|
||||
@@ -1218,7 +1226,7 @@ class AttributeEvents(event.Events):
|
||||
If the listener is registered with ``raw=True``, this will
|
||||
be the :class:`.InstanceState` object.
|
||||
:param value: the value being removed.
|
||||
:param initiator: the attribute implementation object
|
||||
:param initiator: the attribute implementation object
|
||||
which initiated this event.
|
||||
:return: No return value is defined for this event.
|
||||
"""
|
||||
@@ -1231,15 +1239,15 @@ class AttributeEvents(event.Events):
|
||||
be the :class:`.InstanceState` object.
|
||||
:param value: the value being set. If this listener
|
||||
is registered with ``retval=True``, the listener
|
||||
function must return this value, or a new value which
|
||||
function must return this value, or a new value which
|
||||
replaces it.
|
||||
:param oldvalue: the previous value being replaced. This
|
||||
may also be the symbol ``NEVER_SET`` or ``NO_VALUE``.
|
||||
If the listener is registered with ``active_history=True``,
|
||||
the previous value of the attribute will be loaded from
|
||||
the database if the existing value is currently unloaded
|
||||
the database if the existing value is currently unloaded
|
||||
or expired.
|
||||
:param initiator: the attribute implementation object
|
||||
:param initiator: the attribute implementation object
|
||||
which initiated this event.
|
||||
:return: if the event was registered with ``retval=True``,
|
||||
the given value, or a new effective value, should be returned.
|
||||
|
||||
+16
-14
@@ -1,5 +1,5 @@
|
||||
# orm/exc.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -18,21 +18,23 @@ class StaleDataError(sa.exc.SQLAlchemyError):
|
||||
Conditions which cause this to happen include:
|
||||
|
||||
* A flush may have attempted to update or delete rows
|
||||
and an unexpected number of rows were matched during
|
||||
the UPDATE or DELETE statement. Note that when
|
||||
and an unexpected number of rows were matched during
|
||||
the UPDATE or DELETE statement. Note that when
|
||||
version_id_col is used, rows in UPDATE or DELETE statements
|
||||
are also matched against the current known version
|
||||
identifier.
|
||||
|
||||
* A mapped object with version_id_col was refreshed,
|
||||
* A mapped object with version_id_col was refreshed,
|
||||
and the version number coming back from the database does
|
||||
not match that of the object itself.
|
||||
|
||||
|
||||
* A object is detached from its parent object, however
|
||||
the object was previously attached to a different parent
|
||||
identity which was garbage collected, and a decision
|
||||
cannot be made if the new parent was really the most
|
||||
recent "parent" (new in 0.7.4).
|
||||
recent "parent".
|
||||
|
||||
.. versionadded:: 0.7.4
|
||||
|
||||
"""
|
||||
|
||||
@@ -50,7 +52,7 @@ class ObjectDereferencedError(sa.exc.SQLAlchemyError):
|
||||
"""An operation cannot complete due to an object being garbage collected."""
|
||||
|
||||
class DetachedInstanceError(sa.exc.SQLAlchemyError):
|
||||
"""An attempt to access unloaded attributes on a
|
||||
"""An attempt to access unloaded attributes on a
|
||||
mapped instance that is detached."""
|
||||
|
||||
class UnmappedInstanceError(UnmappedError):
|
||||
@@ -89,21 +91,21 @@ class UnmappedClassError(UnmappedError):
|
||||
class ObjectDeletedError(sa.exc.InvalidRequestError):
|
||||
"""A refresh operation failed to retrieve the database
|
||||
row corresponding to an object's known primary key identity.
|
||||
|
||||
A refresh operation proceeds when an expired attribute is
|
||||
|
||||
A refresh operation proceeds when an expired attribute is
|
||||
accessed on an object, or when :meth:`.Query.get` is
|
||||
used to retrieve an object which is, upon retrieval, detected
|
||||
as expired. A SELECT is emitted for the target row
|
||||
based on primary key; if no row is returned, this
|
||||
exception is raised.
|
||||
|
||||
The true meaning of this exception is simply that
|
||||
|
||||
The true meaning of this exception is simply that
|
||||
no row exists for the primary key identifier associated
|
||||
with a persistent object. The row may have been
|
||||
with a persistent object. The row may have been
|
||||
deleted, or in some cases the primary key updated
|
||||
to a new value, outside of the ORM's management of the target
|
||||
object.
|
||||
|
||||
object.
|
||||
|
||||
"""
|
||||
def __init__(self, state, msg=None):
|
||||
if not msg:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# orm/identity.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -128,7 +128,7 @@ class WeakInstanceDict(IdentityMap):
|
||||
o = existing_state._is_really_none()
|
||||
if o is not None:
|
||||
raise AssertionError("A conflicting state is already "
|
||||
"present in the identity map for key %r"
|
||||
"present in the identity map for key %r"
|
||||
% (key, ))
|
||||
else:
|
||||
return
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# orm/instrumentation.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -91,7 +91,7 @@ class ClassManager(dict):
|
||||
self.originals = {}
|
||||
|
||||
self._bases = [mgr for mgr in [
|
||||
manager_of_class(base)
|
||||
manager_of_class(base)
|
||||
for base in self.class_.__bases__
|
||||
if isinstance(base, type)
|
||||
] if mgr is not None]
|
||||
@@ -139,7 +139,7 @@ class ClassManager(dict):
|
||||
|
||||
def _instrument_init(self):
|
||||
# TODO: self.class_.__init__ is often the already-instrumented
|
||||
# __init__ from an instrumented superclass. We still need to make
|
||||
# __init__ from an instrumented superclass. We still need to make
|
||||
# our own wrapper, but it would
|
||||
# be nice to wrap the original __init__ and not our existing wrapper
|
||||
# of such, since this adds method overhead.
|
||||
@@ -212,7 +212,7 @@ class ClassManager(dict):
|
||||
if key in self.mutable_attributes:
|
||||
self.mutable_attributes.remove(key)
|
||||
for cls in self.class_.__subclasses__():
|
||||
manager = manager_of_class(cls)
|
||||
manager = manager_of_class(cls)
|
||||
if manager:
|
||||
manager.uninstrument_attribute(key, True)
|
||||
|
||||
@@ -277,12 +277,12 @@ class ClassManager(dict):
|
||||
|
||||
def new_instance(self, state=None):
|
||||
instance = self.class_.__new__(self.class_)
|
||||
setattr(instance, self.STATE_ATTR,
|
||||
setattr(instance, self.STATE_ATTR,
|
||||
state or self._state_constructor(instance, self))
|
||||
return instance
|
||||
|
||||
def setup_instance(self, instance, state=None):
|
||||
setattr(instance, self.STATE_ATTR,
|
||||
setattr(instance, self.STATE_ATTR,
|
||||
state or self._state_constructor(instance, self))
|
||||
|
||||
def teardown_instance(self, instance):
|
||||
@@ -387,7 +387,7 @@ class _ClassInstrumentationAdapter(ClassManager):
|
||||
if delegate:
|
||||
return delegate(key, state, factory)
|
||||
else:
|
||||
return ClassManager.initialize_collection(self, key,
|
||||
return ClassManager.initialize_collection(self, key,
|
||||
state, factory)
|
||||
|
||||
def new_instance(self, state=None):
|
||||
@@ -463,7 +463,7 @@ def is_instrumented(instance, key):
|
||||
class InstrumentationRegistry(object):
|
||||
"""Private instrumentation registration singleton.
|
||||
|
||||
All classes are routed through this registry
|
||||
All classes are routed through this registry
|
||||
when first instrumented, however the InstrumentationRegistry
|
||||
is not actually needed unless custom ClassManagers are in use.
|
||||
|
||||
@@ -501,7 +501,7 @@ class InstrumentationRegistry(object):
|
||||
|
||||
if factory != ClassManager and not self._extended:
|
||||
# somebody invoked a custom ClassManager.
|
||||
# reinstall global "getter" functions with the more
|
||||
# reinstall global "getter" functions with the more
|
||||
# expensive ones.
|
||||
self._extended = True
|
||||
_install_lookup_strategy(self)
|
||||
@@ -543,7 +543,7 @@ class InstrumentationRegistry(object):
|
||||
return factories
|
||||
|
||||
def manager_of_class(self, cls):
|
||||
# this is only called when alternate instrumentation
|
||||
# this is only called when alternate instrumentation
|
||||
# has been established
|
||||
if cls is None:
|
||||
return None
|
||||
@@ -555,7 +555,7 @@ class InstrumentationRegistry(object):
|
||||
return finder(cls)
|
||||
|
||||
def state_of(self, instance):
|
||||
# this is only called when alternate instrumentation
|
||||
# this is only called when alternate instrumentation
|
||||
# has been established
|
||||
if instance is None:
|
||||
raise AttributeError("None has no persistent state.")
|
||||
@@ -566,7 +566,7 @@ class InstrumentationRegistry(object):
|
||||
instance.__class__)
|
||||
|
||||
def dict_of(self, instance):
|
||||
# this is only called when alternate instrumentation
|
||||
# this is only called when alternate instrumentation
|
||||
# has been established
|
||||
if instance is None:
|
||||
raise AttributeError("None has no persistent state.")
|
||||
@@ -632,7 +632,7 @@ instrumentation_finders.append(find_native_user_instrumentation_hook)
|
||||
def _generate_init(class_, class_manager):
|
||||
"""Build an __init__ decorator that triggers ClassManager events."""
|
||||
|
||||
# TODO: we should use the ClassManager's notion of the
|
||||
# TODO: we should use the ClassManager's notion of the
|
||||
# original '__init__' method, once ClassManager is fixed
|
||||
# to always reference that.
|
||||
original__init__ = class_.__init__
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# orm/interfaces.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -61,13 +61,13 @@ class MapperProperty(object):
|
||||
attribute, as well as that attribute as it appears on individual
|
||||
instances of the class, including attribute instrumentation,
|
||||
attribute access, loading behavior, and dependency calculations.
|
||||
|
||||
|
||||
The most common occurrences of :class:`.MapperProperty` are the
|
||||
mapped :class:`.Column`, which is represented in a mapping as
|
||||
mapped :class:`.Column`, which is represented in a mapping as
|
||||
an instance of :class:`.ColumnProperty`,
|
||||
and a reference to another class produced by :func:`.relationship`,
|
||||
represented in the mapping as an instance of :class:`.RelationshipProperty`.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
cascade = ()
|
||||
@@ -87,7 +87,7 @@ class MapperProperty(object):
|
||||
|
||||
pass
|
||||
|
||||
def create_row_processor(self, context, path, reduced_path,
|
||||
def create_row_processor(self, context, path, reduced_path,
|
||||
mapper, row, adapter):
|
||||
"""Return a 3-tuple consisting of three row processing functions.
|
||||
|
||||
@@ -263,13 +263,13 @@ class PropComparator(operators.ColumnOperators):
|
||||
"""Return true if this collection contains any member that meets the
|
||||
given criterion.
|
||||
|
||||
The usual implementation of ``any()`` is
|
||||
The usual implementation of ``any()`` is
|
||||
:meth:`.RelationshipProperty.Comparator.any`.
|
||||
|
||||
:param criterion: an optional ClauseElement formulated against the
|
||||
:param criterion: an optional ClauseElement formulated against the
|
||||
member class' table or attributes.
|
||||
|
||||
:param \**kwargs: key/value pairs corresponding to member class attribute
|
||||
:param \**kwargs: key/value pairs corresponding to member class attribute
|
||||
names which will be compared via equality to the corresponding
|
||||
values.
|
||||
|
||||
@@ -281,13 +281,13 @@ class PropComparator(operators.ColumnOperators):
|
||||
"""Return true if this element references a member which meets the
|
||||
given criterion.
|
||||
|
||||
The usual implementation of ``has()`` is
|
||||
The usual implementation of ``has()`` is
|
||||
:meth:`.RelationshipProperty.Comparator.has`.
|
||||
|
||||
:param criterion: an optional ClauseElement formulated against the
|
||||
:param criterion: an optional ClauseElement formulated against the
|
||||
member class' table or attributes.
|
||||
|
||||
:param \**kwargs: key/value pairs corresponding to member class attribute
|
||||
:param \**kwargs: key/value pairs corresponding to member class attribute
|
||||
names which will be compared via equality to the corresponding
|
||||
values.
|
||||
|
||||
@@ -337,12 +337,12 @@ class StrategizedProperty(MapperProperty):
|
||||
|
||||
def setup(self, context, entity, path, reduced_path, adapter, **kwargs):
|
||||
self._get_context_strategy(context, reduced_path + (self.key,)).\
|
||||
setup_query(context, entity, path,
|
||||
setup_query(context, entity, path,
|
||||
reduced_path, adapter, **kwargs)
|
||||
|
||||
def create_row_processor(self, context, path, reduced_path, mapper, row, adapter):
|
||||
return self._get_context_strategy(context, reduced_path + (self.key,)).\
|
||||
create_row_processor(context, path,
|
||||
create_row_processor(context, path,
|
||||
reduced_path, mapper, row, adapter)
|
||||
|
||||
def do_init(self):
|
||||
@@ -365,7 +365,7 @@ def serialize_path(path):
|
||||
return None
|
||||
|
||||
return zip(
|
||||
[m.class_ for m in [path[i] for i in range(0, len(path), 2)]],
|
||||
[m.class_ for m in [path[i] for i in range(0, len(path), 2)]],
|
||||
[path[i] for i in range(1, len(path), 2)] + [None]
|
||||
)
|
||||
|
||||
@@ -382,7 +382,7 @@ class MapperOption(object):
|
||||
"""Describe a modification to a Query."""
|
||||
|
||||
propagate_to_loaders = False
|
||||
"""if True, indicate this option should be carried along
|
||||
"""if True, indicate this option should be carried along
|
||||
Query object generated by scalar or object lazy loaders.
|
||||
"""
|
||||
|
||||
@@ -464,9 +464,9 @@ class PropertyOption(MapperOption):
|
||||
else:
|
||||
raise sa_exc.ArgumentError(
|
||||
"Can't find property '%s' on any entity "
|
||||
"specified in this Query. Note the full path "
|
||||
"from root (%s) to target entity must be specified."
|
||||
% (token, ",".join(str(x) for
|
||||
"specified in this Query. Note the full path "
|
||||
"from root (%s) to target entity must be specified."
|
||||
% (token, ",".join(str(x) for
|
||||
x in query._mapper_entities))
|
||||
)
|
||||
else:
|
||||
@@ -494,7 +494,7 @@ class PropertyOption(MapperOption):
|
||||
l = []
|
||||
mappers = []
|
||||
|
||||
# _current_path implies we're in a
|
||||
# _current_path implies we're in a
|
||||
# secondary load with an existing path
|
||||
current_path = list(query._current_path)
|
||||
|
||||
@@ -520,8 +520,8 @@ class PropertyOption(MapperOption):
|
||||
|
||||
if not entity:
|
||||
entity = self._find_entity_basestring(
|
||||
query,
|
||||
token,
|
||||
query,
|
||||
token,
|
||||
raiseerr)
|
||||
if entity is None:
|
||||
return [], []
|
||||
@@ -555,8 +555,8 @@ class PropertyOption(MapperOption):
|
||||
if not entity:
|
||||
entity = self._find_entity_prop_comparator(
|
||||
query,
|
||||
prop.key,
|
||||
token.parententity,
|
||||
prop.key,
|
||||
token.parententity,
|
||||
raiseerr)
|
||||
if not entity:
|
||||
return [], []
|
||||
@@ -587,7 +587,7 @@ class PropertyOption(MapperOption):
|
||||
)
|
||||
|
||||
if current_path:
|
||||
# ran out of tokens before
|
||||
# ran out of tokens before
|
||||
# current_path was exhausted.
|
||||
assert not tokens
|
||||
return [], []
|
||||
@@ -630,9 +630,9 @@ def _reduce_path(path):
|
||||
of the mapper referenced by Mapper.prop1.
|
||||
|
||||
"""
|
||||
return tuple([i % 2 != 0 and
|
||||
element or
|
||||
getattr(element, 'base_mapper', element)
|
||||
return tuple([i % 2 != 0 and
|
||||
element or
|
||||
getattr(element, 'base_mapper', element)
|
||||
for i, element in enumerate(path)])
|
||||
|
||||
class LoaderStrategy(object):
|
||||
@@ -678,7 +678,7 @@ class LoaderStrategy(object):
|
||||
def setup_query(self, context, entity, path, reduced_path, adapter, **kwargs):
|
||||
pass
|
||||
|
||||
def create_row_processor(self, context, path, reduced_path, mapper,
|
||||
def create_row_processor(self, context, path, reduced_path, mapper,
|
||||
row, adapter):
|
||||
"""Return row processing functions which fulfill the contract
|
||||
specified by MapperProperty.create_row_processor.
|
||||
|
||||
+140
-125
@@ -1,5 +1,5 @@
|
||||
# orm/mapper.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -214,16 +214,16 @@ class Mapper(object):
|
||||
local_table = None
|
||||
"""The :class:`.Selectable` which this :class:`.Mapper` manages.
|
||||
|
||||
Typically is an instance of :class:`.Table` or :class:`.Alias`.
|
||||
May also be ``None``.
|
||||
Typically is an instance of :class:`.Table` or :class:`.Alias`.
|
||||
May also be ``None``.
|
||||
|
||||
The "local" table is the
|
||||
selectable that the :class:`.Mapper` is directly responsible for
|
||||
selectable that the :class:`.Mapper` is directly responsible for
|
||||
managing from an attribute access and flush perspective. For
|
||||
non-inheriting mappers, the local table is the same as the
|
||||
"mapped" table. For joined-table inheritance mappers, local_table
|
||||
will be the particular sub-table of the overall "join" which
|
||||
this :class:`.Mapper` represents. If this mapper is a
|
||||
this :class:`.Mapper` represents. If this mapper is a
|
||||
single-table inheriting mapper, local_table will be ``None``.
|
||||
|
||||
See also :attr:`~.Mapper.mapped_table`.
|
||||
@@ -233,11 +233,11 @@ class Mapper(object):
|
||||
mapped_table = None
|
||||
"""The :class:`.Selectable` to which this :class:`.Mapper` is mapped.
|
||||
|
||||
Typically an instance of :class:`.Table`, :class:`.Join`, or
|
||||
Typically an instance of :class:`.Table`, :class:`.Join`, or
|
||||
:class:`.Alias`.
|
||||
|
||||
The "mapped" table is the selectable that
|
||||
the mapper selects from during queries. For non-inheriting
|
||||
The "mapped" table is the selectable that
|
||||
the mapper selects from during queries. For non-inheriting
|
||||
mappers, the mapped table is the same as the "local" table.
|
||||
For joined-table inheritance mappers, mapped_table references the
|
||||
full :class:`.Join` representing full rows for this particular
|
||||
@@ -249,7 +249,7 @@ class Mapper(object):
|
||||
"""
|
||||
|
||||
inherits = None
|
||||
"""References the :class:`.Mapper` which this :class:`.Mapper`
|
||||
"""References the :class:`.Mapper` which this :class:`.Mapper`
|
||||
inherits from, if any.
|
||||
|
||||
This is a *read only* attribute determined during mapper construction.
|
||||
@@ -268,7 +268,7 @@ class Mapper(object):
|
||||
"""
|
||||
|
||||
concrete = None
|
||||
"""Represent ``True`` if this :class:`.Mapper` is a concrete
|
||||
"""Represent ``True`` if this :class:`.Mapper` is a concrete
|
||||
inheritance mapper.
|
||||
|
||||
This is a *read only* attribute determined during mapper construction.
|
||||
@@ -291,7 +291,7 @@ class Mapper(object):
|
||||
|
||||
primary_key = None
|
||||
"""An iterable containing the collection of :class:`.Column` objects
|
||||
which comprise the 'primary key' of the mapped table, from the
|
||||
which comprise the 'primary key' of the mapped table, from the
|
||||
perspective of this :class:`.Mapper`.
|
||||
|
||||
This list is against the selectable in :attr:`~.Mapper.mapped_table`. In the
|
||||
@@ -301,7 +301,7 @@ class Mapper(object):
|
||||
referenced by the :class:`.Join`.
|
||||
|
||||
The list is also not necessarily the same as the primary key column
|
||||
collection associated with the underlying tables; the :class:`.Mapper`
|
||||
collection associated with the underlying tables; the :class:`.Mapper`
|
||||
features a ``primary_key`` argument that can override what the
|
||||
:class:`.Mapper` considers as primary key columns.
|
||||
|
||||
@@ -328,7 +328,7 @@ class Mapper(object):
|
||||
"""
|
||||
|
||||
single = None
|
||||
"""Represent ``True`` if this :class:`.Mapper` is a single table
|
||||
"""Represent ``True`` if this :class:`.Mapper` is a single table
|
||||
inheritance mapper.
|
||||
|
||||
:attr:`~.Mapper.local_table` will be ``None`` if this flag is set.
|
||||
@@ -339,8 +339,8 @@ class Mapper(object):
|
||||
"""
|
||||
|
||||
non_primary = None
|
||||
"""Represent ``True`` if this :class:`.Mapper` is a "non-primary"
|
||||
mapper, e.g. a mapper that is used only to selet rows but not for
|
||||
"""Represent ``True`` if this :class:`.Mapper` is a "non-primary"
|
||||
mapper, e.g. a mapper that is used only to selet rows but not for
|
||||
persistence management.
|
||||
|
||||
This is a *read only* attribute determined during mapper construction.
|
||||
@@ -364,10 +364,10 @@ class Mapper(object):
|
||||
"""A mapping of "polymorphic identity" identifiers mapped to :class:`.Mapper`
|
||||
instances, within an inheritance scenario.
|
||||
|
||||
The identifiers can be of any type which is comparable to the
|
||||
The identifiers can be of any type which is comparable to the
|
||||
type of column represented by :attr:`~.Mapper.polymorphic_on`.
|
||||
|
||||
An inheritance chain of mappers will all reference the same
|
||||
An inheritance chain of mappers will all reference the same
|
||||
polymorphic map object. The object is used to correlate incoming
|
||||
result rows to target mappers.
|
||||
|
||||
@@ -402,10 +402,10 @@ class Mapper(object):
|
||||
"""
|
||||
|
||||
columns = None
|
||||
"""A collection of :class:`.Column` or other scalar expression
|
||||
"""A collection of :class:`.Column` or other scalar expression
|
||||
objects maintained by this :class:`.Mapper`.
|
||||
|
||||
The collection behaves the same as that of the ``c`` attribute on
|
||||
The collection behaves the same as that of the ``c`` attribute on
|
||||
any :class:`.Table` object, except that only those columns included in
|
||||
this mapping are present, and are keyed based on the attribute name
|
||||
defined in the mapping, not necessarily the ``key`` attribute of the
|
||||
@@ -419,11 +419,11 @@ class Mapper(object):
|
||||
|
||||
validators = None
|
||||
"""An immutable dictionary of attributes which have been decorated
|
||||
using the :func:`~.orm.validates` decorator.
|
||||
|
||||
using the :func:`~.orm.validates` decorator.
|
||||
|
||||
The dictionary contains string attribute names as keys
|
||||
mapped to the actual validation method.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
c = None
|
||||
@@ -443,13 +443,13 @@ class Mapper(object):
|
||||
self.inherits = class_mapper(self.inherits, compile=False)
|
||||
if not issubclass(self.class_, self.inherits.class_):
|
||||
raise sa_exc.ArgumentError(
|
||||
"Class '%s' does not inherit from '%s'" %
|
||||
"Class '%s' does not inherit from '%s'" %
|
||||
(self.class_.__name__, self.inherits.class_.__name__))
|
||||
if self.non_primary != self.inherits.non_primary:
|
||||
np = not self.non_primary and "primary" or "non-primary"
|
||||
raise sa_exc.ArgumentError(
|
||||
"Inheritance of %s mapper for class '%s' is "
|
||||
"only allowed from a %s mapper" %
|
||||
"only allowed from a %s mapper" %
|
||||
(np, self.class_.__name__, np))
|
||||
# inherit_condition is optional.
|
||||
if self.local_table is None:
|
||||
@@ -472,7 +472,7 @@ class Mapper(object):
|
||||
self.inherits.local_table,
|
||||
self.local_table)
|
||||
self.mapped_table = sql.join(
|
||||
self.inherits.mapped_table,
|
||||
self.inherits.mapped_table,
|
||||
self.local_table,
|
||||
self.inherit_condition)
|
||||
|
||||
@@ -499,7 +499,7 @@ class Mapper(object):
|
||||
"the inherited versioning column. "
|
||||
"version_id_col should only be specified on "
|
||||
"the base-most mapper that includes versioning." %
|
||||
(self.version_id_col.description,
|
||||
(self.version_id_col.description,
|
||||
self.inherits.version_id_col.description)
|
||||
)
|
||||
|
||||
@@ -528,7 +528,7 @@ class Mapper(object):
|
||||
|
||||
if self.mapped_table is None:
|
||||
raise sa_exc.ArgumentError(
|
||||
"Mapper '%s' does not have a mapped_table specified."
|
||||
"Mapper '%s' does not have a mapped_table specified."
|
||||
% self)
|
||||
|
||||
def _set_with_polymorphic(self, with_polymorphic):
|
||||
@@ -580,6 +580,12 @@ class Mapper(object):
|
||||
self.inherits._inheriting_mappers.add(self)
|
||||
self.passive_updates = self.inherits.passive_updates
|
||||
self._all_tables = self.inherits._all_tables
|
||||
for key, prop in mapper._props.iteritems():
|
||||
if key not in self._props and \
|
||||
not self._should_exclude(key, key, local=False,
|
||||
column=None):
|
||||
self._adapt_inherited_property(key, prop, False)
|
||||
|
||||
|
||||
def _set_polymorphic_on(self, polymorphic_on):
|
||||
self.polymorphic_on = polymorphic_on
|
||||
@@ -589,7 +595,7 @@ class Mapper(object):
|
||||
|
||||
if self.inherits:
|
||||
self.dispatch._update(self.inherits.dispatch)
|
||||
super_extensions = set(chain(*[m._deprecated_extensions
|
||||
super_extensions = set(chain(*[m._deprecated_extensions
|
||||
for m in self.inherits.iterate_to_root()]))
|
||||
else:
|
||||
super_extensions = set()
|
||||
@@ -600,7 +606,7 @@ class Mapper(object):
|
||||
|
||||
def _configure_listeners(self):
|
||||
if self.inherits:
|
||||
super_extensions = set(chain(*[m._deprecated_extensions
|
||||
super_extensions = set(chain(*[m._deprecated_extensions
|
||||
for m in self.inherits.iterate_to_root()]))
|
||||
else:
|
||||
super_extensions = set()
|
||||
@@ -647,8 +653,8 @@ class Mapper(object):
|
||||
"remove *all* current mappers from all classes." %
|
||||
self.class_)
|
||||
#else:
|
||||
# a ClassManager may already exist as
|
||||
# ClassManager.instrument_attribute() creates
|
||||
# a ClassManager may already exist as
|
||||
# ClassManager.instrument_attribute() creates
|
||||
# new managers for each subclass if they don't yet exist.
|
||||
|
||||
_mapper_registry[self] = True
|
||||
@@ -662,8 +668,8 @@ class Mapper(object):
|
||||
|
||||
manager.mapper = self
|
||||
manager.deferred_scalar_loader = self._load_scalar_attributes
|
||||
|
||||
# The remaining members can be added by any mapper,
|
||||
|
||||
# The remaining members can be added by any mapper,
|
||||
# e_name None or not.
|
||||
if manager.info.get(_INSTRUMENTOR, False):
|
||||
return
|
||||
@@ -678,9 +684,10 @@ class Mapper(object):
|
||||
self._reconstructor = method
|
||||
event.listen(manager, 'load', _event_on_load, raw=True)
|
||||
elif hasattr(method, '__sa_validators__'):
|
||||
include_removes = getattr(method, "__sa_include_removes__", False)
|
||||
for name in method.__sa_validators__:
|
||||
self.validators = self.validators.union(
|
||||
{name : method}
|
||||
{name : (method, include_removes)}
|
||||
)
|
||||
|
||||
manager.info[_INSTRUMENTOR] = self
|
||||
@@ -746,10 +753,10 @@ class Mapper(object):
|
||||
self._readonly_props = set(
|
||||
self._columntoproperty[col]
|
||||
for col in self._columntoproperty
|
||||
if not hasattr(col, 'table') or
|
||||
if not hasattr(col, 'table') or
|
||||
col.table not in self._cols_by_table)
|
||||
|
||||
# if explicit PK argument sent, add those columns to the
|
||||
# if explicit PK argument sent, add those columns to the
|
||||
# primary key mappings
|
||||
if self._primary_key_argument:
|
||||
for k in self._primary_key_argument:
|
||||
@@ -762,23 +769,23 @@ class Mapper(object):
|
||||
len(self._pks_by_table[self.mapped_table]) == 0:
|
||||
raise sa_exc.ArgumentError(
|
||||
"Mapper %s could not assemble any primary "
|
||||
"key columns for mapped table '%s'" %
|
||||
"key columns for mapped table '%s'" %
|
||||
(self, self.mapped_table.description))
|
||||
elif self.local_table not in self._pks_by_table and \
|
||||
isinstance(self.local_table, schema.Table):
|
||||
util.warn("Could not assemble any primary "
|
||||
"keys for locally mapped table '%s' - "
|
||||
"no rows will be persisted in this Table."
|
||||
"no rows will be persisted in this Table."
|
||||
% self.local_table.description)
|
||||
|
||||
if self.inherits and \
|
||||
not self.concrete and \
|
||||
not self._primary_key_argument:
|
||||
# if inheriting, the "primary key" for this mapper is
|
||||
# if inheriting, the "primary key" for this mapper is
|
||||
# that of the inheriting (unless concrete or explicit)
|
||||
self.primary_key = self.inherits.primary_key
|
||||
else:
|
||||
# determine primary key from argument or mapped_table pks -
|
||||
# determine primary key from argument or mapped_table pks -
|
||||
# reduce to the minimal set of columns
|
||||
if self._primary_key_argument:
|
||||
primary_key = sqlutil.reduce_columns(
|
||||
@@ -793,7 +800,7 @@ class Mapper(object):
|
||||
if len(primary_key) == 0:
|
||||
raise sa_exc.ArgumentError(
|
||||
"Mapper %s could not assemble any primary "
|
||||
"key columns for mapped table '%s'" %
|
||||
"key columns for mapped table '%s'" %
|
||||
(self, self.mapped_table.description))
|
||||
|
||||
self.primary_key = tuple(primary_key)
|
||||
@@ -845,19 +852,19 @@ class Mapper(object):
|
||||
if column in mapper._columntoproperty:
|
||||
column_key = mapper._columntoproperty[column].key
|
||||
|
||||
self._configure_property(column_key,
|
||||
column,
|
||||
init=False,
|
||||
self._configure_property(column_key,
|
||||
column,
|
||||
init=False,
|
||||
setparent=True)
|
||||
|
||||
def _configure_polymorphic_setter(self, init=False):
|
||||
"""Configure an attribute on the mapper representing the
|
||||
'polymorphic_on' column, if applicable, and not
|
||||
"""Configure an attribute on the mapper representing the
|
||||
'polymorphic_on' column, if applicable, and not
|
||||
already generated by _configure_properties (which is typical).
|
||||
|
||||
Also create a setter function which will assign this
|
||||
attribute to the value of the 'polymorphic_identity'
|
||||
upon instance construction, also if applicable. This
|
||||
upon instance construction, also if applicable. This
|
||||
routine will run when an instance is created.
|
||||
|
||||
"""
|
||||
@@ -906,15 +913,15 @@ class Mapper(object):
|
||||
else:
|
||||
# polymorphic_on is a Column or SQL expression and doesn't
|
||||
# appear to be mapped.
|
||||
# this means it can be 1. only present in the with_polymorphic
|
||||
# this means it can be 1. only present in the with_polymorphic
|
||||
# selectable or 2. a totally standalone SQL expression which we'd
|
||||
# hope is compatible with this mapper's mapped_table
|
||||
col = self.mapped_table.corresponding_column(self.polymorphic_on)
|
||||
if col is None:
|
||||
# polymorphic_on doesn't derive from any column/expression
|
||||
# polymorphic_on doesn't derive from any column/expression
|
||||
# isn't present in the mapped table.
|
||||
# we will make a "hidden" ColumnProperty for it.
|
||||
# Just check that if it's directly a schema.Column and we
|
||||
# we will make a "hidden" ColumnProperty for it.
|
||||
# Just check that if it's directly a schema.Column and we
|
||||
# have with_polymorphic, it's likely a user error if the
|
||||
# schema.Column isn't represented somehow in either mapped_table or
|
||||
# with_polymorphic. Otherwise as of 0.7.4 we just go with it
|
||||
@@ -932,15 +939,14 @@ class Mapper(object):
|
||||
"loads will not function properly"
|
||||
% col.description)
|
||||
else:
|
||||
# column/expression that polymorphic_on derives from
|
||||
# column/expression that polymorphic_on derives from
|
||||
# is present in our mapped table
|
||||
# and is probably mapped, but polymorphic_on itself
|
||||
# is not. This happens when
|
||||
# the polymorphic_on is only directly present in the
|
||||
# is not. This happens when
|
||||
# the polymorphic_on is only directly present in the
|
||||
# with_polymorphic selectable, as when use polymorphic_union.
|
||||
# we'll make a separate ColumnProperty for it.
|
||||
instrument = True
|
||||
|
||||
key = getattr(col, 'key', None)
|
||||
if key:
|
||||
if self._should_exclude(col.key, col.key, False, col):
|
||||
@@ -952,7 +958,7 @@ class Mapper(object):
|
||||
key = col.key
|
||||
|
||||
self._configure_property(
|
||||
key,
|
||||
key,
|
||||
properties.ColumnProperty(col, _instrument=instrument),
|
||||
init=init, setparent=True)
|
||||
polymorphic_key = key
|
||||
@@ -998,15 +1004,15 @@ class Mapper(object):
|
||||
self._configure_property(key, prop, init=False, setparent=False)
|
||||
elif key not in self._props:
|
||||
self._configure_property(
|
||||
key,
|
||||
properties.ConcreteInheritedProperty(),
|
||||
key,
|
||||
properties.ConcreteInheritedProperty(),
|
||||
init=init, setparent=True)
|
||||
|
||||
def _configure_property(self, key, prop, init=True, setparent=True):
|
||||
self._log("_configure_property(%s, %s)", key, prop.__class__.__name__)
|
||||
|
||||
if not isinstance(prop, MapperProperty):
|
||||
# we were passed a Column or a list of Columns;
|
||||
# we were passed a Column or a list of Columns;
|
||||
# generate a properties.ColumnProperty
|
||||
columns = util.to_list(prop)
|
||||
column = columns[0]
|
||||
@@ -1026,7 +1032,7 @@ class Mapper(object):
|
||||
"explicitly."
|
||||
% (prop.columns[-1], column, key))
|
||||
|
||||
# existing properties.ColumnProperty from an inheriting
|
||||
# existing properties.ColumnProperty from an inheriting
|
||||
# mapper. make a copy and append our column to it
|
||||
prop = prop.copy()
|
||||
prop.columns.insert(0, column)
|
||||
@@ -1065,14 +1071,14 @@ class Mapper(object):
|
||||
"(including its availability as a foreign key), "
|
||||
"use the 'include_properties' or 'exclude_properties' "
|
||||
"mapper arguments to control specifically which table "
|
||||
"columns get mapped." %
|
||||
"columns get mapped." %
|
||||
(key, self, column.key, prop))
|
||||
|
||||
if isinstance(prop, properties.ColumnProperty):
|
||||
col = self.mapped_table.corresponding_column(prop.columns[0])
|
||||
|
||||
# if the column is not present in the mapped table,
|
||||
# test if a column has been added after the fact to the
|
||||
# if the column is not present in the mapped table,
|
||||
# test if a column has been added after the fact to the
|
||||
# parent table (or their parent, etc.) [ticket:1570]
|
||||
if col is None and self.inherits:
|
||||
path = [self]
|
||||
@@ -1086,20 +1092,20 @@ class Mapper(object):
|
||||
break
|
||||
path.append(m)
|
||||
|
||||
# subquery expression, column not present in the mapped
|
||||
# subquery expression, column not present in the mapped
|
||||
# selectable.
|
||||
if col is None:
|
||||
col = prop.columns[0]
|
||||
|
||||
# column is coming in after _readonly_props was
|
||||
# column is coming in after _readonly_props was
|
||||
# initialized; check for 'readonly'
|
||||
if hasattr(self, '_readonly_props') and \
|
||||
(not hasattr(col, 'table') or
|
||||
(not hasattr(col, 'table') or
|
||||
col.table not in self._cols_by_table):
|
||||
self._readonly_props.add(prop)
|
||||
|
||||
else:
|
||||
# if column is coming in after _cols_by_table was
|
||||
# if column is coming in after _cols_by_table was
|
||||
# initialized, ensure the col is in the right set
|
||||
if hasattr(self, '_cols_by_table') and \
|
||||
col.table in self._cols_by_table and \
|
||||
@@ -1199,10 +1205,10 @@ class Mapper(object):
|
||||
def _log_desc(self):
|
||||
return "(" + self.class_.__name__ + \
|
||||
"|" + \
|
||||
(self.local_table is not None and
|
||||
self.local_table.description or
|
||||
(self.local_table is not None and
|
||||
self.local_table.description or
|
||||
str(self.local_table)) +\
|
||||
(self.non_primary and
|
||||
(self.non_primary and
|
||||
"|non-primary" or "") + ")"
|
||||
|
||||
def _log(self, msg, *args):
|
||||
@@ -1223,7 +1229,7 @@ class Mapper(object):
|
||||
def __str__(self):
|
||||
return "Mapper|%s|%s%s" % (
|
||||
self.class_.__name__,
|
||||
self.local_table is not None and
|
||||
self.local_table is not None and
|
||||
self.local_table.description or None,
|
||||
self.non_primary and "|non-primary" or ""
|
||||
)
|
||||
@@ -1288,7 +1294,7 @@ class Mapper(object):
|
||||
for m in mappers:
|
||||
if not m.isa(self):
|
||||
raise sa_exc.InvalidRequestError(
|
||||
"%r does not inherit from %r" %
|
||||
"%r does not inherit from %r" %
|
||||
(m, self))
|
||||
else:
|
||||
mappers = []
|
||||
@@ -1387,7 +1393,7 @@ class Mapper(object):
|
||||
mappers])
|
||||
):
|
||||
if getattr(c, '_is_polymorphic_discriminator', False) and \
|
||||
(self.polymorphic_on is None or
|
||||
(self.polymorphic_on is None or
|
||||
c.columns[0] is not self.polymorphic_on):
|
||||
continue
|
||||
yield c
|
||||
@@ -1452,7 +1458,7 @@ class Mapper(object):
|
||||
return result
|
||||
|
||||
def _is_userland_descriptor(self, obj):
|
||||
if isinstance(obj, (MapperProperty,
|
||||
if isinstance(obj, (MapperProperty,
|
||||
attributes.QueryableAttribute)):
|
||||
return False
|
||||
elif not hasattr(obj, '__get__'):
|
||||
@@ -1505,7 +1511,7 @@ class Mapper(object):
|
||||
return False
|
||||
|
||||
def common_parent(self, other):
|
||||
"""Return true if the given mapper shares a
|
||||
"""Return true if the given mapper shares a
|
||||
common inherited parent as this mapper."""
|
||||
|
||||
return self.base_mapper is other.base_mapper
|
||||
@@ -1634,7 +1640,7 @@ class Mapper(object):
|
||||
for col in self.primary_key
|
||||
]
|
||||
|
||||
def _get_state_attr_by_column(self, state, dict_, column,
|
||||
def _get_state_attr_by_column(self, state, dict_, column,
|
||||
passive=attributes.PASSIVE_OFF):
|
||||
prop = self._columntoproperty[column]
|
||||
return state.manager[prop.key].impl.get(state, dict_, passive=passive)
|
||||
@@ -1648,7 +1654,7 @@ class Mapper(object):
|
||||
dict_ = attributes.instance_dict(obj)
|
||||
return self._get_committed_state_attr_by_column(state, dict_, column)
|
||||
|
||||
def _get_committed_state_attr_by_column(self, state, dict_,
|
||||
def _get_committed_state_attr_by_column(self, state, dict_,
|
||||
column, passive=attributes.PASSIVE_OFF):
|
||||
|
||||
prop = self._columntoproperty[column]
|
||||
@@ -1674,8 +1680,8 @@ class Mapper(object):
|
||||
statement = self._optimized_get_statement(state, attribute_names)
|
||||
if statement is not None:
|
||||
result = session.query(self).from_statement(statement).\
|
||||
_load_on_ident(None,
|
||||
only_load_props=attribute_names,
|
||||
_load_on_ident(None,
|
||||
only_load_props=attribute_names,
|
||||
refresh_state=state)
|
||||
|
||||
if result is False:
|
||||
@@ -1698,16 +1704,16 @@ class Mapper(object):
|
||||
_none_set.issuperset(identity_key):
|
||||
util.warn("Instance %s to be refreshed doesn't "
|
||||
"contain a full primary key - can't be refreshed "
|
||||
"(and shouldn't be expired, either)."
|
||||
"(and shouldn't be expired, either)."
|
||||
% state_str(state))
|
||||
return
|
||||
|
||||
result = session.query(self)._load_on_ident(
|
||||
identity_key,
|
||||
refresh_state=state,
|
||||
identity_key,
|
||||
refresh_state=state,
|
||||
only_load_props=attribute_names)
|
||||
|
||||
# if instance is pending, a refresh operation
|
||||
# if instance is pending, a refresh operation
|
||||
# may not complete (even if PK attributes are assigned)
|
||||
if has_key and result is None:
|
||||
raise orm_exc.ObjectDeletedError(state)
|
||||
@@ -1716,16 +1722,16 @@ class Mapper(object):
|
||||
"""assemble a WHERE clause which retrieves a given state by primary
|
||||
key, using a minimized set of tables.
|
||||
|
||||
Applies to a joined-table inheritance mapper where the
|
||||
Applies to a joined-table inheritance mapper where the
|
||||
requested attribute names are only present on joined tables,
|
||||
not the base table. The WHERE clause attempts to include
|
||||
not the base table. The WHERE clause attempts to include
|
||||
only those tables to minimize joins.
|
||||
|
||||
"""
|
||||
props = self._props
|
||||
|
||||
tables = set(chain(
|
||||
*[sqlutil.find_tables(c, check_columns=True)
|
||||
*[sqlutil.find_tables(c, check_columns=True)
|
||||
for key in attribute_names
|
||||
for c in props[key].columns]
|
||||
))
|
||||
@@ -1744,8 +1750,8 @@ class Mapper(object):
|
||||
|
||||
if leftcol.table not in tables:
|
||||
leftval = self._get_committed_state_attr_by_column(
|
||||
state, state.dict,
|
||||
leftcol,
|
||||
state, state.dict,
|
||||
leftcol,
|
||||
passive=attributes.PASSIVE_NO_INITIALIZE)
|
||||
if leftval is attributes.PASSIVE_NO_RESULT or leftval is None:
|
||||
raise ColumnsNotAvailable()
|
||||
@@ -1753,8 +1759,8 @@ class Mapper(object):
|
||||
type_=binary.right.type)
|
||||
elif rightcol.table not in tables:
|
||||
rightval = self._get_committed_state_attr_by_column(
|
||||
state, state.dict,
|
||||
rightcol,
|
||||
state, state.dict,
|
||||
rightcol,
|
||||
passive=attributes.PASSIVE_NO_INITIALIZE)
|
||||
if rightval is attributes.PASSIVE_NO_RESULT or rightval is None:
|
||||
raise ColumnsNotAvailable()
|
||||
@@ -1770,8 +1776,8 @@ class Mapper(object):
|
||||
start = True
|
||||
if start and not mapper.single:
|
||||
allconds.append(visitors.cloned_traverse(
|
||||
mapper.inherit_condition,
|
||||
{},
|
||||
mapper.inherit_condition,
|
||||
{},
|
||||
{'binary':visit_binary}
|
||||
)
|
||||
)
|
||||
@@ -1804,7 +1810,7 @@ class Mapper(object):
|
||||
visited_states = set()
|
||||
prp, mpp = object(), object()
|
||||
|
||||
visitables = deque([(deque(self._props.values()), prp,
|
||||
visitables = deque([(deque(self._props.values()), prp,
|
||||
state, state.dict)])
|
||||
|
||||
while visitables:
|
||||
@@ -1817,7 +1823,7 @@ class Mapper(object):
|
||||
prop = iterator.popleft()
|
||||
if type_ not in prop.cascade:
|
||||
continue
|
||||
queue = deque(prop.cascade_iterator(type_, parent_state,
|
||||
queue = deque(prop.cascade_iterator(type_, parent_state,
|
||||
parent_dict, visited_states, halt_on))
|
||||
if queue:
|
||||
visitables.append((queue,mpp, None, None))
|
||||
@@ -1826,8 +1832,8 @@ class Mapper(object):
|
||||
corresponding_dict = iterator.popleft()
|
||||
yield instance, instance_mapper, \
|
||||
corresponding_state, corresponding_dict
|
||||
visitables.append((deque(instance_mapper._props.values()),
|
||||
prp, corresponding_state,
|
||||
visitables.append((deque(instance_mapper._props.values()),
|
||||
prp, corresponding_state,
|
||||
corresponding_dict))
|
||||
|
||||
@_memoized_configured_property
|
||||
@@ -1884,7 +1890,7 @@ class Mapper(object):
|
||||
|
||||
@util.memoized_property
|
||||
def _table_to_equated(self):
|
||||
"""memoized map of tables to collections of columns to be
|
||||
"""memoized map of tables to collections of columns to be
|
||||
synchronized upwards to the base mapper."""
|
||||
|
||||
result = util.defaultdict(list)
|
||||
@@ -1900,16 +1906,16 @@ class Mapper(object):
|
||||
return result
|
||||
|
||||
|
||||
def _instance_processor(self, context, path, reduced_path, adapter,
|
||||
polymorphic_from=None,
|
||||
def _instance_processor(self, context, path, reduced_path, adapter,
|
||||
polymorphic_from=None,
|
||||
only_load_props=None, refresh_state=None,
|
||||
polymorphic_discriminator=None):
|
||||
|
||||
"""Produce a mapper level row processor callable
|
||||
"""Produce a mapper level row processor callable
|
||||
which processes rows into mapped instances."""
|
||||
|
||||
# note that this method, most of which exists in a closure
|
||||
# called _instance(), resists being broken out, as
|
||||
# called _instance(), resists being broken out, as
|
||||
# attempts to do so tend to add significant function
|
||||
# call overhead. _instance() is the most
|
||||
# performance-critical section in the whole ORM.
|
||||
@@ -2019,7 +2025,7 @@ class Mapper(object):
|
||||
identitykey = self._identity_key_from_state(refresh_state)
|
||||
else:
|
||||
identitykey = (
|
||||
identity_class,
|
||||
identity_class,
|
||||
tuple([row[column] for column in pk_cols])
|
||||
)
|
||||
|
||||
@@ -2036,22 +2042,22 @@ class Mapper(object):
|
||||
version_id_col is not None and \
|
||||
context.version_check and \
|
||||
self._get_state_attr_by_column(
|
||||
state,
|
||||
dict_,
|
||||
state,
|
||||
dict_,
|
||||
self.version_id_col) != \
|
||||
row[version_id_col]:
|
||||
|
||||
raise orm_exc.StaleDataError(
|
||||
"Instance '%s' has version id '%s' which "
|
||||
"does not match database-loaded version id '%s'."
|
||||
% (state_str(state),
|
||||
"does not match database-loaded version id '%s'."
|
||||
% (state_str(state),
|
||||
self._get_state_attr_by_column(
|
||||
state, dict_,
|
||||
self.version_id_col),
|
||||
row[version_id_col]))
|
||||
elif refresh_state:
|
||||
# out of band refresh_state detected (i.e. its not in the
|
||||
# session.identity_map) honor it anyway. this can happen
|
||||
# session.identity_map) honor it anyway. this can happen
|
||||
# if a _get() occurs within save_obj(), such as
|
||||
# when eager_defaults is True.
|
||||
state = refresh_state
|
||||
@@ -2072,7 +2078,7 @@ class Mapper(object):
|
||||
|
||||
if create_instance:
|
||||
for fn in create_instance:
|
||||
instance = fn(self, context,
|
||||
instance = fn(self, context,
|
||||
row, self.class_)
|
||||
if instance is not EXT_CONTINUE:
|
||||
manager = attributes.manager_of_class(
|
||||
@@ -2103,8 +2109,8 @@ class Mapper(object):
|
||||
|
||||
if populate_instance:
|
||||
for fn in populate_instance:
|
||||
ret = fn(self, context, row, state,
|
||||
only_load_props=only_load_props,
|
||||
ret = fn(self, context, row, state,
|
||||
only_load_props=only_load_props,
|
||||
instancekey=identitykey, isnew=isnew)
|
||||
if ret is not EXT_CONTINUE:
|
||||
break
|
||||
@@ -2132,8 +2138,8 @@ class Mapper(object):
|
||||
|
||||
if populate_instance:
|
||||
for fn in populate_instance:
|
||||
ret = fn(self, context, row, state,
|
||||
only_load_props=attrs,
|
||||
ret = fn(self, context, row, state,
|
||||
only_load_props=attrs,
|
||||
instancekey=identitykey, isnew=isnew)
|
||||
if ret is not EXT_CONTINUE:
|
||||
break
|
||||
@@ -2153,7 +2159,7 @@ class Mapper(object):
|
||||
if result is not None:
|
||||
if append_result:
|
||||
for fn in append_result:
|
||||
if fn(self, context, row, state,
|
||||
if fn(self, context, row, state,
|
||||
result, instancekey=identitykey,
|
||||
isnew=isnew) is not EXT_CONTINUE:
|
||||
break
|
||||
@@ -2173,7 +2179,7 @@ class Mapper(object):
|
||||
pops = (new_populators, existing_populators, delayed_populators, eager_populators)
|
||||
for prop in self._props.itervalues():
|
||||
for i, pop in enumerate(prop.create_row_processor(
|
||||
context, path,
|
||||
context, path,
|
||||
reduced_path,
|
||||
self, row, adapter)):
|
||||
if pop is not None:
|
||||
@@ -2196,8 +2202,8 @@ class Mapper(object):
|
||||
if mapper is self:
|
||||
return None
|
||||
|
||||
# replace the tip of the path info with the subclass mapper
|
||||
# being used. that way accurate "load_path" info is available
|
||||
# replace the tip of the path info with the subclass mapper
|
||||
# being used. that way accurate "load_path" info is available
|
||||
# for options invoked during deferred loads.
|
||||
# we lose AliasedClass path elements this way, but currently,
|
||||
# those are not needed at this stage.
|
||||
@@ -2205,7 +2211,7 @@ class Mapper(object):
|
||||
# this asserts to true
|
||||
#assert mapper.isa(_class_to_mapper(path[-1]))
|
||||
|
||||
return mapper._instance_processor(context, path[0:-1] + (mapper,),
|
||||
return mapper._instance_processor(context, path[0:-1] + (mapper,),
|
||||
reduced_path[0:-1] + (mapper.base_mapper,),
|
||||
adapter,
|
||||
polymorphic_from=self)
|
||||
@@ -2217,14 +2223,14 @@ def configure_mappers():
|
||||
"""Initialize the inter-mapper relationships of all mappers that
|
||||
have been constructed thus far.
|
||||
|
||||
This function can be called any number of times, but in
|
||||
This function can be called any number of times, but in
|
||||
most cases is handled internally.
|
||||
|
||||
"""
|
||||
|
||||
global _new_mappers
|
||||
if not _new_mappers:
|
||||
return
|
||||
return
|
||||
|
||||
_call_configured = None
|
||||
_COMPILE_MUTEX.acquire()
|
||||
@@ -2240,8 +2246,8 @@ def configure_mappers():
|
||||
return
|
||||
|
||||
# initialize properties on all mappers
|
||||
# note that _mapper_registry is unordered, which
|
||||
# may randomly conceal/reveal issues related to
|
||||
# note that _mapper_registry is unordered, which
|
||||
# may randomly conceal/reveal issues related to
|
||||
# the order of mapper compilation
|
||||
for mapper in list(_mapper_registry):
|
||||
if getattr(mapper, '_configure_failed', False):
|
||||
@@ -2291,7 +2297,7 @@ def reconstructor(fn):
|
||||
fn.__sa_reconstructor__ = True
|
||||
return fn
|
||||
|
||||
def validates(*names):
|
||||
def validates(*names, **kw):
|
||||
"""Decorate a method as a 'validator' for one or more named properties.
|
||||
|
||||
Designates a method as a validator, a method which receives the
|
||||
@@ -2307,9 +2313,18 @@ def validates(*names):
|
||||
an assertion to avoid recursion overflows. This is a reentrant
|
||||
condition which is not supported.
|
||||
|
||||
:param \*names: list of attribute names to be validated.
|
||||
:param include_removes: if True, "remove" events will be
|
||||
sent as well - the validation function must accept an additional
|
||||
argument "is_remove" which will be a boolean.
|
||||
|
||||
.. versionadded:: 0.7.7
|
||||
|
||||
"""
|
||||
include_removes = kw.pop('include_removes', False)
|
||||
def wrap(fn):
|
||||
fn.__sa_validators__ = names
|
||||
fn.__sa_include_removes__ = include_removes
|
||||
return fn
|
||||
return wrap
|
||||
|
||||
@@ -2320,7 +2335,7 @@ def _event_on_load(state, ctx):
|
||||
|
||||
def _event_on_first_init(manager, cls):
|
||||
"""Initial mapper compilation trigger.
|
||||
|
||||
|
||||
instrumentation calls this one when InstanceState
|
||||
is first generated, and is needed for legacy mutable
|
||||
attributes to work.
|
||||
@@ -2333,11 +2348,11 @@ def _event_on_first_init(manager, cls):
|
||||
|
||||
def _event_on_init(state, args, kwargs):
|
||||
"""Run init_instance hooks.
|
||||
|
||||
|
||||
This also includes mapper compilation, normally not needed
|
||||
here but helps with some piecemeal configuration
|
||||
scenarios (such as in the ORM tutorial).
|
||||
|
||||
|
||||
"""
|
||||
|
||||
instrumenting_mapper = state.manager.info.get(_INSTRUMENTOR)
|
||||
|
||||
+109
-107
@@ -1,5 +1,5 @@
|
||||
# orm/persistence.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -23,7 +23,7 @@ from sqlalchemy.orm import attributes, sync, \
|
||||
from sqlalchemy.orm.util import _state_mapper, state_str
|
||||
|
||||
def save_obj(base_mapper, states, uowtransaction, single=False):
|
||||
"""Issue ``INSERT`` and/or ``UPDATE`` statements for a list
|
||||
"""Issue ``INSERT`` and/or ``UPDATE`` statements for a list
|
||||
of objects.
|
||||
|
||||
This is called within the context of a UOWTransaction during a
|
||||
@@ -40,30 +40,30 @@ def save_obj(base_mapper, states, uowtransaction, single=False):
|
||||
return
|
||||
|
||||
states_to_insert, states_to_update = _organize_states_for_save(
|
||||
base_mapper,
|
||||
states,
|
||||
base_mapper,
|
||||
states,
|
||||
uowtransaction)
|
||||
|
||||
cached_connections = _cached_connection_dict(base_mapper)
|
||||
|
||||
for table, mapper in base_mapper._sorted_tables.iteritems():
|
||||
insert = _collect_insert_commands(base_mapper, uowtransaction,
|
||||
insert = _collect_insert_commands(base_mapper, uowtransaction,
|
||||
table, states_to_insert)
|
||||
|
||||
update = _collect_update_commands(base_mapper, uowtransaction,
|
||||
update = _collect_update_commands(base_mapper, uowtransaction,
|
||||
table, states_to_update)
|
||||
|
||||
if update:
|
||||
_emit_update_statements(base_mapper, uowtransaction,
|
||||
cached_connections,
|
||||
_emit_update_statements(base_mapper, uowtransaction,
|
||||
cached_connections,
|
||||
mapper, table, update)
|
||||
|
||||
if insert:
|
||||
_emit_insert_statements(base_mapper, uowtransaction,
|
||||
cached_connections,
|
||||
_emit_insert_statements(base_mapper, uowtransaction,
|
||||
cached_connections,
|
||||
table, insert)
|
||||
|
||||
_finalize_insert_update_commands(base_mapper, uowtransaction,
|
||||
_finalize_insert_update_commands(base_mapper, uowtransaction,
|
||||
states_to_insert, states_to_update)
|
||||
|
||||
def post_update(base_mapper, states, uowtransaction, post_update_cols):
|
||||
@@ -74,18 +74,18 @@ def post_update(base_mapper, states, uowtransaction, post_update_cols):
|
||||
cached_connections = _cached_connection_dict(base_mapper)
|
||||
|
||||
states_to_update = _organize_states_for_post_update(
|
||||
base_mapper,
|
||||
base_mapper,
|
||||
states, uowtransaction)
|
||||
|
||||
|
||||
for table, mapper in base_mapper._sorted_tables.iteritems():
|
||||
update = _collect_post_update_commands(base_mapper, uowtransaction,
|
||||
table, states_to_update,
|
||||
update = _collect_post_update_commands(base_mapper, uowtransaction,
|
||||
table, states_to_update,
|
||||
post_update_cols)
|
||||
|
||||
if update:
|
||||
_emit_post_update_statements(base_mapper, uowtransaction,
|
||||
cached_connections,
|
||||
_emit_post_update_statements(base_mapper, uowtransaction,
|
||||
cached_connections,
|
||||
mapper, table, update)
|
||||
|
||||
def delete_obj(base_mapper, states, uowtransaction):
|
||||
@@ -99,19 +99,19 @@ def delete_obj(base_mapper, states, uowtransaction):
|
||||
cached_connections = _cached_connection_dict(base_mapper)
|
||||
|
||||
states_to_delete = _organize_states_for_delete(
|
||||
base_mapper,
|
||||
base_mapper,
|
||||
states,
|
||||
uowtransaction)
|
||||
|
||||
table_to_mapper = base_mapper._sorted_tables
|
||||
|
||||
for table in reversed(table_to_mapper.keys()):
|
||||
delete = _collect_delete_commands(base_mapper, uowtransaction,
|
||||
delete = _collect_delete_commands(base_mapper, uowtransaction,
|
||||
table, states_to_delete)
|
||||
|
||||
mapper = table_to_mapper[table]
|
||||
|
||||
_emit_delete_statements(base_mapper, uowtransaction,
|
||||
_emit_delete_statements(base_mapper, uowtransaction,
|
||||
cached_connections, mapper, table, delete)
|
||||
|
||||
for state, state_dict, mapper, has_identity, connection \
|
||||
@@ -121,20 +121,20 @@ def delete_obj(base_mapper, states, uowtransaction):
|
||||
def _organize_states_for_save(base_mapper, states, uowtransaction):
|
||||
"""Make an initial pass across a set of states for INSERT or
|
||||
UPDATE.
|
||||
|
||||
|
||||
This includes splitting out into distinct lists for
|
||||
each, calling before_insert/before_update, obtaining
|
||||
key information for each state including its dictionary,
|
||||
mapper, the connection to use for the execution per state,
|
||||
and the identity flag.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
states_to_insert = []
|
||||
states_to_update = []
|
||||
|
||||
for state, dict_, mapper, connection in _connections_for_states(
|
||||
base_mapper, uowtransaction,
|
||||
base_mapper, uowtransaction,
|
||||
states):
|
||||
|
||||
has_identity = bool(state.key)
|
||||
@@ -148,9 +148,9 @@ def _organize_states_for_save(base_mapper, states, uowtransaction):
|
||||
else:
|
||||
mapper.dispatch.before_update(mapper, connection, state)
|
||||
|
||||
# detect if we have a "pending" instance (i.e. has
|
||||
# no instance_key attached to it), and another instance
|
||||
# with the same identity key already exists as persistent.
|
||||
# detect if we have a "pending" instance (i.e. has
|
||||
# no instance_key attached to it), and another instance
|
||||
# with the same identity key already exists as persistent.
|
||||
# convert to an UPDATE if so.
|
||||
if not has_identity and \
|
||||
instance_key in uowtransaction.session.identity_map:
|
||||
@@ -160,14 +160,14 @@ def _organize_states_for_save(base_mapper, states, uowtransaction):
|
||||
if not uowtransaction.is_deleted(existing):
|
||||
raise orm_exc.FlushError(
|
||||
"New instance %s with identity key %s conflicts "
|
||||
"with persistent instance %s" %
|
||||
"with persistent instance %s" %
|
||||
(state_str(state), instance_key,
|
||||
state_str(existing)))
|
||||
|
||||
base_mapper._log_debug(
|
||||
"detected row switch for identity %s. "
|
||||
"will update %s, remove %s from "
|
||||
"transaction", instance_key,
|
||||
"transaction", instance_key,
|
||||
state_str(state), state_str(existing))
|
||||
|
||||
# remove the "delete" flag from the existing element
|
||||
@@ -176,55 +176,55 @@ def _organize_states_for_save(base_mapper, states, uowtransaction):
|
||||
|
||||
if not has_identity and not row_switch:
|
||||
states_to_insert.append(
|
||||
(state, dict_, mapper, connection,
|
||||
(state, dict_, mapper, connection,
|
||||
has_identity, instance_key, row_switch)
|
||||
)
|
||||
else:
|
||||
states_to_update.append(
|
||||
(state, dict_, mapper, connection,
|
||||
(state, dict_, mapper, connection,
|
||||
has_identity, instance_key, row_switch)
|
||||
)
|
||||
|
||||
return states_to_insert, states_to_update
|
||||
|
||||
def _organize_states_for_post_update(base_mapper, states,
|
||||
def _organize_states_for_post_update(base_mapper, states,
|
||||
uowtransaction):
|
||||
"""Make an initial pass across a set of states for UPDATE
|
||||
corresponding to post_update.
|
||||
|
||||
This includes obtaining key information for each state
|
||||
including its dictionary, mapper, the connection to use for
|
||||
|
||||
This includes obtaining key information for each state
|
||||
including its dictionary, mapper, the connection to use for
|
||||
the execution per state.
|
||||
|
||||
|
||||
"""
|
||||
return list(_connections_for_states(base_mapper, uowtransaction,
|
||||
return list(_connections_for_states(base_mapper, uowtransaction,
|
||||
states))
|
||||
|
||||
def _organize_states_for_delete(base_mapper, states, uowtransaction):
|
||||
"""Make an initial pass across a set of states for DELETE.
|
||||
|
||||
|
||||
This includes calling out before_delete and obtaining
|
||||
key information for each state including its dictionary,
|
||||
mapper, the connection to use for the execution per state.
|
||||
|
||||
|
||||
"""
|
||||
states_to_delete = []
|
||||
|
||||
for state, dict_, mapper, connection in _connections_for_states(
|
||||
base_mapper, uowtransaction,
|
||||
base_mapper, uowtransaction,
|
||||
states):
|
||||
|
||||
mapper.dispatch.before_delete(mapper, connection, state)
|
||||
|
||||
states_to_delete.append((state, dict_, mapper,
|
||||
states_to_delete.append((state, dict_, mapper,
|
||||
bool(state.key), connection))
|
||||
return states_to_delete
|
||||
|
||||
def _collect_insert_commands(base_mapper, uowtransaction, table,
|
||||
def _collect_insert_commands(base_mapper, uowtransaction, table,
|
||||
states_to_insert):
|
||||
"""Identify sets of values to use in INSERT statements for a
|
||||
list of states.
|
||||
|
||||
|
||||
"""
|
||||
insert = []
|
||||
for state, state_dict, mapper, connection, has_identity, \
|
||||
@@ -242,7 +242,7 @@ def _collect_insert_commands(base_mapper, uowtransaction, table,
|
||||
if col is mapper.version_id_col:
|
||||
params[col.key] = mapper.version_id_generator(None)
|
||||
else:
|
||||
# pull straight from the dict for
|
||||
# pull straight from the dict for
|
||||
# pending objects
|
||||
prop = mapper._columntoproperty[col]
|
||||
value = state_dict.get(prop.key, None)
|
||||
@@ -259,15 +259,15 @@ def _collect_insert_commands(base_mapper, uowtransaction, table,
|
||||
else:
|
||||
params[col.key] = value
|
||||
|
||||
insert.append((state, state_dict, params, mapper,
|
||||
insert.append((state, state_dict, params, mapper,
|
||||
connection, value_params, has_all_pks))
|
||||
return insert
|
||||
|
||||
def _collect_update_commands(base_mapper, uowtransaction,
|
||||
def _collect_update_commands(base_mapper, uowtransaction,
|
||||
table, states_to_update):
|
||||
"""Identify sets of values to use in UPDATE statements for a
|
||||
list of states.
|
||||
|
||||
|
||||
This function works intricately with the history system
|
||||
to determine exactly what values should be updated
|
||||
as well as how the row should be matched within an UPDATE
|
||||
@@ -292,14 +292,14 @@ def _collect_update_commands(base_mapper, uowtransaction,
|
||||
if col is mapper.version_id_col:
|
||||
params[col._label] = \
|
||||
mapper._get_committed_state_attr_by_column(
|
||||
row_switch or state,
|
||||
row_switch and row_switch.dict
|
||||
row_switch or state,
|
||||
row_switch and row_switch.dict
|
||||
or state_dict,
|
||||
col)
|
||||
|
||||
prop = mapper._columntoproperty[col]
|
||||
history = attributes.get_state_history(
|
||||
state, prop.key,
|
||||
state, prop.key,
|
||||
attributes.PASSIVE_NO_INITIALIZE
|
||||
)
|
||||
if history.added:
|
||||
@@ -309,20 +309,20 @@ def _collect_update_commands(base_mapper, uowtransaction,
|
||||
params[col.key] = mapper.version_id_generator(
|
||||
params[col._label])
|
||||
|
||||
# HACK: check for history, in case the
|
||||
# HACK: check for history, in case the
|
||||
# history is only
|
||||
# in a different table than the one
|
||||
# in a different table than the one
|
||||
# where the version_id_col is.
|
||||
for prop in mapper._columntoproperty.itervalues():
|
||||
history = attributes.get_state_history(
|
||||
state, prop.key,
|
||||
state, prop.key,
|
||||
attributes.PASSIVE_NO_INITIALIZE)
|
||||
if history.added:
|
||||
hasdata = True
|
||||
else:
|
||||
prop = mapper._columntoproperty[col]
|
||||
history = attributes.get_state_history(
|
||||
state, prop.key,
|
||||
state, prop.key,
|
||||
attributes.PASSIVE_NO_INITIALIZE)
|
||||
if history.added:
|
||||
if isinstance(history.added[0],
|
||||
@@ -344,7 +344,7 @@ def _collect_update_commands(base_mapper, uowtransaction,
|
||||
value = history.added[0]
|
||||
params[col._label] = value
|
||||
else:
|
||||
# use the old value to
|
||||
# use the old value to
|
||||
# locate the row
|
||||
value = history.deleted[0]
|
||||
params[col._label] = value
|
||||
@@ -374,12 +374,12 @@ def _collect_update_commands(base_mapper, uowtransaction,
|
||||
"Can't update table "
|
||||
"using NULL for primary "
|
||||
"key value")
|
||||
update.append((state, state_dict, params, mapper,
|
||||
update.append((state, state_dict, params, mapper,
|
||||
connection, value_params))
|
||||
return update
|
||||
|
||||
|
||||
def _collect_post_update_commands(base_mapper, uowtransaction, table,
|
||||
def _collect_post_update_commands(base_mapper, uowtransaction, table,
|
||||
states_to_update, post_update_cols):
|
||||
"""Identify sets of values to use in UPDATE statements for a
|
||||
list of states within a post_update operation.
|
||||
@@ -403,20 +403,20 @@ def _collect_post_update_commands(base_mapper, uowtransaction, table,
|
||||
elif col in post_update_cols:
|
||||
prop = mapper._columntoproperty[col]
|
||||
history = attributes.get_state_history(
|
||||
state, prop.key,
|
||||
state, prop.key,
|
||||
attributes.PASSIVE_NO_INITIALIZE)
|
||||
if history.added:
|
||||
value = history.added[0]
|
||||
params[col.key] = value
|
||||
hasdata = True
|
||||
if hasdata:
|
||||
update.append((state, state_dict, params, mapper,
|
||||
update.append((state, state_dict, params, mapper,
|
||||
connection))
|
||||
return update
|
||||
|
||||
def _collect_delete_commands(base_mapper, uowtransaction, table,
|
||||
def _collect_delete_commands(base_mapper, uowtransaction, table,
|
||||
states_to_delete):
|
||||
"""Identify values to use in DELETE statements for a list of
|
||||
"""Identify values to use in DELETE statements for a list of
|
||||
states to be deleted."""
|
||||
|
||||
delete = util.defaultdict(list)
|
||||
@@ -448,7 +448,7 @@ def _collect_delete_commands(base_mapper, uowtransaction, table,
|
||||
return delete
|
||||
|
||||
|
||||
def _emit_update_statements(base_mapper, uowtransaction,
|
||||
def _emit_update_statements(base_mapper, uowtransaction,
|
||||
cached_connections, mapper, table, update):
|
||||
"""Emit UPDATE statements corresponding to value lists collected
|
||||
by _collect_update_commands()."""
|
||||
@@ -466,7 +466,7 @@ def _emit_update_statements(base_mapper, uowtransaction,
|
||||
if needs_version_id:
|
||||
clause.clauses.append(mapper.version_id_col ==\
|
||||
sql.bindparam(mapper.version_id_col._label,
|
||||
type_=col.type))
|
||||
type_=mapper.version_id_col.type))
|
||||
|
||||
return table.update(clause)
|
||||
|
||||
@@ -486,13 +486,13 @@ def _emit_update_statements(base_mapper, uowtransaction,
|
||||
|
||||
_postfetch(
|
||||
mapper,
|
||||
uowtransaction,
|
||||
table,
|
||||
state,
|
||||
state_dict,
|
||||
c.context.prefetch_cols,
|
||||
uowtransaction,
|
||||
table,
|
||||
state,
|
||||
state_dict,
|
||||
c.context.prefetch_cols,
|
||||
c.context.postfetch_cols,
|
||||
c.context.compiled_parameters[0],
|
||||
c.context.compiled_parameters[0],
|
||||
value_params)
|
||||
rows += c.rowcount
|
||||
|
||||
@@ -505,11 +505,11 @@ def _emit_update_statements(base_mapper, uowtransaction,
|
||||
|
||||
elif needs_version_id:
|
||||
util.warn("Dialect %s does not support updated rowcount "
|
||||
"- versioning cannot be verified." %
|
||||
"- versioning cannot be verified." %
|
||||
c.dialect.dialect_description,
|
||||
stacklevel=12)
|
||||
|
||||
def _emit_insert_statements(base_mapper, uowtransaction,
|
||||
def _emit_insert_statements(base_mapper, uowtransaction,
|
||||
cached_connections, table, insert):
|
||||
"""Emit INSERT statements corresponding to value lists collected
|
||||
by _collect_insert_commands()."""
|
||||
@@ -517,10 +517,10 @@ def _emit_insert_statements(base_mapper, uowtransaction,
|
||||
statement = base_mapper._memo(('insert', table), table.insert)
|
||||
|
||||
for (connection, pkeys, hasvalue, has_all_pks), \
|
||||
records in groupby(insert,
|
||||
lambda rec: (rec[4],
|
||||
rec[2].keys(),
|
||||
bool(rec[5]),
|
||||
records in groupby(insert,
|
||||
lambda rec: (rec[4],
|
||||
rec[2].keys(),
|
||||
bool(rec[5]),
|
||||
rec[6])
|
||||
):
|
||||
if has_all_pks and not hasvalue:
|
||||
@@ -529,19 +529,19 @@ def _emit_insert_statements(base_mapper, uowtransaction,
|
||||
c = cached_connections[connection].\
|
||||
execute(statement, multiparams)
|
||||
|
||||
for (state, state_dict, params, mapper,
|
||||
for (state, state_dict, params, mapper,
|
||||
conn, value_params, has_all_pks), \
|
||||
last_inserted_params in \
|
||||
zip(records, c.context.compiled_parameters):
|
||||
_postfetch(
|
||||
mapper,
|
||||
uowtransaction,
|
||||
uowtransaction,
|
||||
table,
|
||||
state,
|
||||
state,
|
||||
state_dict,
|
||||
c.context.prefetch_cols,
|
||||
c.context.postfetch_cols,
|
||||
last_inserted_params,
|
||||
last_inserted_params,
|
||||
value_params)
|
||||
|
||||
else:
|
||||
@@ -561,31 +561,31 @@ def _emit_insert_statements(base_mapper, uowtransaction,
|
||||
|
||||
if primary_key is not None:
|
||||
# set primary key attributes
|
||||
for pk, col in zip(primary_key,
|
||||
for pk, col in zip(primary_key,
|
||||
mapper._pks_by_table[table]):
|
||||
prop = mapper._columntoproperty[col]
|
||||
if state_dict.get(prop.key) is None:
|
||||
# TODO: would rather say:
|
||||
#state_dict[prop.key] = pk
|
||||
mapper._set_state_attr_by_column(
|
||||
state,
|
||||
state_dict,
|
||||
state,
|
||||
state_dict,
|
||||
col, pk)
|
||||
|
||||
_postfetch(
|
||||
mapper,
|
||||
uowtransaction,
|
||||
table,
|
||||
state,
|
||||
uowtransaction,
|
||||
table,
|
||||
state,
|
||||
state_dict,
|
||||
result.context.prefetch_cols,
|
||||
result.context.prefetch_cols,
|
||||
result.context.postfetch_cols,
|
||||
result.context.compiled_parameters[0],
|
||||
result.context.compiled_parameters[0],
|
||||
value_params)
|
||||
|
||||
|
||||
|
||||
def _emit_post_update_statements(base_mapper, uowtransaction,
|
||||
def _emit_post_update_statements(base_mapper, uowtransaction,
|
||||
cached_connections, mapper, table, update):
|
||||
"""Emit UPDATE statements corresponding to value lists collected
|
||||
by _collect_post_update_commands()."""
|
||||
@@ -603,19 +603,19 @@ def _emit_post_update_statements(base_mapper, uowtransaction,
|
||||
|
||||
# execute each UPDATE in the order according to the original
|
||||
# list of states to guarantee row access order, but
|
||||
# also group them into common (connection, cols) sets
|
||||
# also group them into common (connection, cols) sets
|
||||
# to support executemany().
|
||||
for key, grouper in groupby(
|
||||
update, lambda rec: (rec[4], rec[2].keys())
|
||||
):
|
||||
connection = key[0]
|
||||
multiparams = [params for state, state_dict,
|
||||
multiparams = [params for state, state_dict,
|
||||
params, mapper, conn in grouper]
|
||||
cached_connections[connection].\
|
||||
execute(statement, multiparams)
|
||||
|
||||
|
||||
def _emit_delete_statements(base_mapper, uowtransaction, cached_connections,
|
||||
def _emit_delete_statements(base_mapper, uowtransaction, cached_connections,
|
||||
mapper, table, delete):
|
||||
"""Emit DELETE statements corresponding to value lists collected
|
||||
by _collect_delete_commands()."""
|
||||
@@ -631,9 +631,9 @@ def _emit_delete_statements(base_mapper, uowtransaction, cached_connections,
|
||||
|
||||
if need_version_id:
|
||||
clause.clauses.append(
|
||||
mapper.version_id_col ==
|
||||
mapper.version_id_col ==
|
||||
sql.bindparam(
|
||||
mapper.version_id_col.key,
|
||||
mapper.version_id_col.key,
|
||||
type_=mapper.version_id_col.type
|
||||
)
|
||||
)
|
||||
@@ -657,13 +657,13 @@ def _emit_delete_statements(base_mapper, uowtransaction, cached_connections,
|
||||
if rows != len(del_objects):
|
||||
raise orm_exc.StaleDataError(
|
||||
"DELETE statement on table '%s' expected to "
|
||||
"delete %d row(s); %d were matched." %
|
||||
"delete %d row(s); %d were matched." %
|
||||
(table.description, len(del_objects), c.rowcount)
|
||||
)
|
||||
else:
|
||||
util.warn(
|
||||
"Dialect %s does not support deleted rowcount "
|
||||
"- versioning cannot be verified." %
|
||||
"- versioning cannot be verified." %
|
||||
connection.dialect.dialect_description,
|
||||
stacklevel=12)
|
||||
connection.execute(statement, del_objects)
|
||||
@@ -671,11 +671,11 @@ def _emit_delete_statements(base_mapper, uowtransaction, cached_connections,
|
||||
connection.execute(statement, del_objects)
|
||||
|
||||
|
||||
def _finalize_insert_update_commands(base_mapper, uowtransaction,
|
||||
def _finalize_insert_update_commands(base_mapper, uowtransaction,
|
||||
states_to_insert, states_to_update):
|
||||
"""finalize state on states that have been inserted or updated,
|
||||
including calling after_insert/after_update events.
|
||||
|
||||
|
||||
"""
|
||||
for state, state_dict, mapper, connection, has_identity, \
|
||||
instance_key, row_switch in states_to_insert + \
|
||||
@@ -683,7 +683,7 @@ def _finalize_insert_update_commands(base_mapper, uowtransaction,
|
||||
|
||||
if mapper._readonly_props:
|
||||
readonly = state.unmodified_intersection(
|
||||
[p.key for p in mapper._readonly_props
|
||||
[p.key for p in mapper._readonly_props
|
||||
if p.expire_on_flush or p.key not in state.dict]
|
||||
)
|
||||
if readonly:
|
||||
@@ -703,7 +703,7 @@ def _finalize_insert_update_commands(base_mapper, uowtransaction,
|
||||
else:
|
||||
mapper.dispatch.after_update(mapper, connection, state)
|
||||
|
||||
def _postfetch(mapper, uowtransaction, table,
|
||||
def _postfetch(mapper, uowtransaction, table,
|
||||
state, dict_, prefetch_cols, postfetch_cols,
|
||||
params, value_params):
|
||||
"""Expire attributes in need of newly persisted database state,
|
||||
@@ -718,9 +718,9 @@ def _postfetch(mapper, uowtransaction, table,
|
||||
mapper._set_state_attr_by_column(state, dict_, c, params[c.key])
|
||||
|
||||
if postfetch_cols:
|
||||
state.expire_attributes(state.dict,
|
||||
[mapper._columntoproperty[c].key
|
||||
for c in postfetch_cols if c in
|
||||
state.expire_attributes(state.dict,
|
||||
[mapper._columntoproperty[c].key
|
||||
for c in postfetch_cols if c in
|
||||
mapper._columntoproperty]
|
||||
)
|
||||
|
||||
@@ -728,33 +728,35 @@ def _postfetch(mapper, uowtransaction, table,
|
||||
# TODO: this still goes a little too often. would be nice to
|
||||
# have definitive list of "columns that changed" here
|
||||
for m, equated_pairs in mapper._table_to_equated[table]:
|
||||
sync.populate(state, m, state, m,
|
||||
equated_pairs,
|
||||
sync.populate(state, m, state, m,
|
||||
equated_pairs,
|
||||
uowtransaction,
|
||||
mapper.passive_updates)
|
||||
|
||||
def _connections_for_states(base_mapper, uowtransaction, states):
|
||||
"""Return an iterator of (state, state.dict, mapper, connection).
|
||||
|
||||
|
||||
The states are sorted according to _sort_states, then paired
|
||||
with the connection they should be using for the given
|
||||
unit of work transaction.
|
||||
|
||||
|
||||
"""
|
||||
# if session has a connection callable,
|
||||
# organize individual states with the connection
|
||||
# organize individual states with the connection
|
||||
# to use for update
|
||||
if uowtransaction.session.connection_callable:
|
||||
connection_callable = \
|
||||
uowtransaction.session.connection_callable
|
||||
else:
|
||||
connection = uowtransaction.transaction.connection(
|
||||
base_mapper)
|
||||
connection = None
|
||||
connection_callable = None
|
||||
|
||||
for state in _sort_states(states):
|
||||
if connection_callable:
|
||||
connection = connection_callable(base_mapper, state.obj())
|
||||
elif not connection:
|
||||
connection = uowtransaction.transaction.connection(
|
||||
base_mapper)
|
||||
|
||||
mapper = _state_mapper(state)
|
||||
|
||||
|
||||
+139
-144
@@ -1,5 +1,5 @@
|
||||
# orm/properties.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -33,9 +33,9 @@ from descriptor_props import CompositeProperty, SynonymProperty, \
|
||||
|
||||
class ColumnProperty(StrategizedProperty):
|
||||
"""Describes an object attribute that corresponds to a table column.
|
||||
|
||||
|
||||
Public constructor is the :func:`.orm.column_property` function.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, *columns, **kwargs):
|
||||
@@ -62,7 +62,7 @@ class ColumnProperty(StrategizedProperty):
|
||||
|
||||
"""
|
||||
self._orig_columns = [expression._labeled(c) for c in columns]
|
||||
self.columns = [expression._labeled(_orm_deannotate(c))
|
||||
self.columns = [expression._labeled(_orm_deannotate(c))
|
||||
for c in columns]
|
||||
self.group = kwargs.pop('group', None)
|
||||
self.deferred = kwargs.pop('deferred', False)
|
||||
@@ -88,7 +88,7 @@ class ColumnProperty(StrategizedProperty):
|
||||
if kwargs:
|
||||
raise TypeError(
|
||||
"%s received unexpected keyword argument(s): %s" % (
|
||||
self.__class__.__name__,
|
||||
self.__class__.__name__,
|
||||
', '.join(sorted(kwargs.keys()))))
|
||||
|
||||
util.set_creation_order(self)
|
||||
@@ -104,9 +104,9 @@ class ColumnProperty(StrategizedProperty):
|
||||
return
|
||||
|
||||
attributes.register_descriptor(
|
||||
mapper.class_,
|
||||
self.key,
|
||||
comparator=self.comparator_factory(self, mapper),
|
||||
mapper.class_,
|
||||
self.key,
|
||||
comparator=self.comparator_factory(self, mapper),
|
||||
parententity=mapper,
|
||||
doc=self.doc
|
||||
)
|
||||
@@ -124,19 +124,21 @@ class ColumnProperty(StrategizedProperty):
|
||||
|
||||
def copy(self):
|
||||
return ColumnProperty(
|
||||
deferred=self.deferred,
|
||||
group=self.group,
|
||||
deferred=self.deferred,
|
||||
group=self.group,
|
||||
active_history=self.active_history,
|
||||
*self.columns)
|
||||
|
||||
def _getcommitted(self, state, dict_, column,
|
||||
def _getcommitted(self, state, dict_, column,
|
||||
passive=attributes.PASSIVE_OFF):
|
||||
return state.get_impl(self.key).\
|
||||
get_committed_value(state, dict_, passive=passive)
|
||||
|
||||
def merge(self, session, source_state, source_dict, dest_state,
|
||||
def merge(self, session, source_state, source_dict, dest_state,
|
||||
dest_dict, load, _recursive):
|
||||
if self.key in source_dict:
|
||||
if not self.instrument:
|
||||
return
|
||||
elif self.key in source_dict:
|
||||
value = source_dict[self.key]
|
||||
|
||||
if not load:
|
||||
@@ -144,9 +146,8 @@ class ColumnProperty(StrategizedProperty):
|
||||
else:
|
||||
impl = dest_state.get_impl(self.key)
|
||||
impl.set(dest_state, dest_dict, value, None)
|
||||
else:
|
||||
if dest_state.has_identity and self.key not in dest_dict:
|
||||
dest_state.expire_attributes(dest_dict, [self.key])
|
||||
elif dest_state.has_identity and self.key not in dest_dict:
|
||||
dest_state.expire_attributes(dest_dict, [self.key])
|
||||
|
||||
class Comparator(PropComparator):
|
||||
@util.memoized_instancemethod
|
||||
@@ -176,20 +177,20 @@ log.class_logger(ColumnProperty)
|
||||
class RelationshipProperty(StrategizedProperty):
|
||||
"""Describes an object property that holds a single item or list
|
||||
of items that correspond to a related database table.
|
||||
|
||||
|
||||
Public constructor is the :func:`.orm.relationship` function.
|
||||
|
||||
|
||||
Of note here is the :class:`.RelationshipProperty.Comparator`
|
||||
class, which implements comparison operations for scalar-
|
||||
and collection-referencing mapped attributes.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
strategy_wildcard_key = 'relationship:*'
|
||||
|
||||
def __init__(self, argument,
|
||||
secondary=None, primaryjoin=None,
|
||||
secondaryjoin=None,
|
||||
secondaryjoin=None,
|
||||
foreign_keys=None,
|
||||
uselist=None,
|
||||
order_by=False,
|
||||
@@ -207,7 +208,7 @@ class RelationshipProperty(StrategizedProperty):
|
||||
active_history=False,
|
||||
cascade_backrefs=True,
|
||||
load_on_pending=False,
|
||||
strategy_class=None, _local_remote_pairs=None,
|
||||
strategy_class=None, _local_remote_pairs=None,
|
||||
query_class=None):
|
||||
|
||||
self.uselist = uselist
|
||||
@@ -256,7 +257,7 @@ class RelationshipProperty(StrategizedProperty):
|
||||
self.cascade = CascadeOptions("save-update, merge")
|
||||
|
||||
if self.passive_deletes == 'all' and \
|
||||
("delete" in self.cascade or
|
||||
("delete" in self.cascade or
|
||||
"delete-orphan" in self.cascade):
|
||||
raise sa_exc.ArgumentError(
|
||||
"Can't set passive_deletes='all' in conjunction "
|
||||
@@ -278,9 +279,9 @@ class RelationshipProperty(StrategizedProperty):
|
||||
|
||||
def instrument_class(self, mapper):
|
||||
attributes.register_descriptor(
|
||||
mapper.class_,
|
||||
self.key,
|
||||
comparator=self.comparator_factory(self, mapper),
|
||||
mapper.class_,
|
||||
self.key,
|
||||
comparator=self.comparator_factory(self, mapper),
|
||||
parententity=mapper,
|
||||
doc=self.doc,
|
||||
)
|
||||
@@ -292,7 +293,7 @@ class RelationshipProperty(StrategizedProperty):
|
||||
def __init__(self, prop, mapper, of_type=None, adapter=None):
|
||||
"""Construction of :class:`.RelationshipProperty.Comparator`
|
||||
is internal to the ORM's attribute mechanics.
|
||||
|
||||
|
||||
"""
|
||||
self.prop = prop
|
||||
self.mapper = mapper
|
||||
@@ -322,29 +323,23 @@ class RelationshipProperty(StrategizedProperty):
|
||||
else:
|
||||
return elem
|
||||
|
||||
def operate(self, op, *other, **kwargs):
|
||||
return op(self, *other, **kwargs)
|
||||
|
||||
def reverse_operate(self, op, other, **kwargs):
|
||||
return op(self, *other, **kwargs)
|
||||
|
||||
def of_type(self, cls):
|
||||
"""Produce a construct that represents a particular 'subtype' of
|
||||
attribute for the parent class.
|
||||
|
||||
|
||||
Currently this is usable in conjunction with :meth:`.Query.join`
|
||||
and :meth:`.Query.outerjoin`.
|
||||
|
||||
|
||||
"""
|
||||
return RelationshipProperty.Comparator(
|
||||
self.property,
|
||||
self.mapper,
|
||||
self.property,
|
||||
self.mapper,
|
||||
cls, adapter=self.adapter)
|
||||
|
||||
def in_(self, other):
|
||||
"""Produce an IN clause - this is not implemented
|
||||
"""Produce an IN clause - this is not implemented
|
||||
for :func:`~.orm.relationship`-based attributes at this time.
|
||||
|
||||
|
||||
"""
|
||||
raise NotImplementedError('in_() not yet supported for '
|
||||
'relationships. For a simple many-to-one, use '
|
||||
@@ -361,20 +356,20 @@ class RelationshipProperty(StrategizedProperty):
|
||||
|
||||
this will typically produce a
|
||||
clause such as::
|
||||
|
||||
|
||||
mytable.related_id == <some id>
|
||||
|
||||
Where ``<some id>`` is the primary key of the given
|
||||
|
||||
Where ``<some id>`` is the primary key of the given
|
||||
object.
|
||||
|
||||
|
||||
The ``==`` operator provides partial functionality for non-
|
||||
many-to-one comparisons:
|
||||
|
||||
|
||||
* Comparisons against collections are not supported.
|
||||
Use :meth:`~.RelationshipProperty.Comparator.contains`.
|
||||
* Compared to a scalar one-to-many, will produce a
|
||||
* Compared to a scalar one-to-many, will produce a
|
||||
clause that compares the target columns in the parent to
|
||||
the given target.
|
||||
the given target.
|
||||
* Compared to a scalar many-to-many, an alias
|
||||
of the association table will be rendered as
|
||||
well, forming a natural join that is part of the
|
||||
@@ -448,9 +443,9 @@ class RelationshipProperty(StrategizedProperty):
|
||||
# limit this adapter to annotated only?
|
||||
criterion = target_adapter.traverse(criterion)
|
||||
|
||||
# only have the "joined left side" of what we
|
||||
# only have the "joined left side" of what we
|
||||
# return be subject to Query adaption. The right
|
||||
# side of it is used for an exists() subquery and
|
||||
# side of it is used for an exists() subquery and
|
||||
# should not correlate or otherwise reach out
|
||||
# to anything in the enclosing query.
|
||||
if criterion is not None:
|
||||
@@ -464,42 +459,42 @@ class RelationshipProperty(StrategizedProperty):
|
||||
def any(self, criterion=None, **kwargs):
|
||||
"""Produce an expression that tests a collection against
|
||||
particular criterion, using EXISTS.
|
||||
|
||||
|
||||
An expression like::
|
||||
|
||||
|
||||
session.query(MyClass).filter(
|
||||
MyClass.somereference.any(SomeRelated.x==2)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
Will produce a query like::
|
||||
|
||||
|
||||
SELECT * FROM my_table WHERE
|
||||
EXISTS (SELECT 1 FROM related WHERE related.my_id=my_table.id
|
||||
EXISTS (SELECT 1 FROM related WHERE related.my_id=my_table.id
|
||||
AND related.x=2)
|
||||
|
||||
|
||||
Because :meth:`~.RelationshipProperty.Comparator.any` uses
|
||||
a correlated subquery, its performance is not nearly as
|
||||
good when compared against large target tables as that of
|
||||
using a join.
|
||||
|
||||
|
||||
:meth:`~.RelationshipProperty.Comparator.any` is particularly
|
||||
useful for testing for empty collections::
|
||||
|
||||
|
||||
session.query(MyClass).filter(
|
||||
~MyClass.somereference.any()
|
||||
)
|
||||
|
||||
|
||||
will produce::
|
||||
|
||||
|
||||
SELECT * FROM my_table WHERE
|
||||
NOT EXISTS (SELECT 1 FROM related WHERE related.my_id=my_table.id)
|
||||
|
||||
|
||||
:meth:`~.RelationshipProperty.Comparator.any` is only
|
||||
valid for collections, i.e. a :func:`.relationship`
|
||||
that has ``uselist=True``. For scalar references,
|
||||
use :meth:`~.RelationshipProperty.Comparator.has`.
|
||||
|
||||
|
||||
"""
|
||||
if not self.property.uselist:
|
||||
raise sa_exc.InvalidRequestError(
|
||||
@@ -514,14 +509,14 @@ class RelationshipProperty(StrategizedProperty):
|
||||
particular criterion, using EXISTS.
|
||||
|
||||
An expression like::
|
||||
|
||||
|
||||
session.query(MyClass).filter(
|
||||
MyClass.somereference.has(SomeRelated.x==2)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
Will produce a query like::
|
||||
|
||||
|
||||
SELECT * FROM my_table WHERE
|
||||
EXISTS (SELECT 1 FROM related WHERE related.id==my_table.related_id
|
||||
AND related.x=2)
|
||||
@@ -530,12 +525,12 @@ class RelationshipProperty(StrategizedProperty):
|
||||
a correlated subquery, its performance is not nearly as
|
||||
good when compared against large target tables as that of
|
||||
using a join.
|
||||
|
||||
|
||||
:meth:`~.RelationshipProperty.Comparator.has` is only
|
||||
valid for scalar references, i.e. a :func:`.relationship`
|
||||
that has ``uselist=False``. For collection references,
|
||||
use :meth:`~.RelationshipProperty.Comparator.any`.
|
||||
|
||||
|
||||
"""
|
||||
if self.property.uselist:
|
||||
raise sa_exc.InvalidRequestError(
|
||||
@@ -544,46 +539,46 @@ class RelationshipProperty(StrategizedProperty):
|
||||
return self._criterion_exists(criterion, **kwargs)
|
||||
|
||||
def contains(self, other, **kwargs):
|
||||
"""Return a simple expression that tests a collection for
|
||||
"""Return a simple expression that tests a collection for
|
||||
containment of a particular item.
|
||||
|
||||
|
||||
:meth:`~.RelationshipProperty.Comparator.contains` is
|
||||
only valid for a collection, i.e. a
|
||||
:func:`~.orm.relationship` that implements
|
||||
one-to-many or many-to-many with ``uselist=True``.
|
||||
|
||||
When used in a simple one-to-many context, an
|
||||
|
||||
When used in a simple one-to-many context, an
|
||||
expression like::
|
||||
|
||||
|
||||
MyClass.contains(other)
|
||||
|
||||
|
||||
Produces a clause like::
|
||||
|
||||
|
||||
mytable.id == <some id>
|
||||
|
||||
|
||||
Where ``<some id>`` is the value of the foreign key
|
||||
attribute on ``other`` which refers to the primary
|
||||
key of its parent object. From this it follows that
|
||||
:meth:`~.RelationshipProperty.Comparator.contains` is
|
||||
very useful when used with simple one-to-many
|
||||
operations.
|
||||
|
||||
|
||||
For many-to-many operations, the behavior of
|
||||
:meth:`~.RelationshipProperty.Comparator.contains`
|
||||
has more caveats. The association table will be
|
||||
rendered in the statement, producing an "implicit"
|
||||
join, that is, includes multiple tables in the FROM
|
||||
clause which are equated in the WHERE clause::
|
||||
|
||||
|
||||
query(MyClass).filter(MyClass.contains(other))
|
||||
|
||||
|
||||
Produces a query like::
|
||||
|
||||
|
||||
SELECT * FROM my_table, my_association_table AS
|
||||
my_association_table_1 WHERE
|
||||
my_table.id = my_association_table_1.parent_id
|
||||
AND my_association_table_1.child_id = <some id>
|
||||
|
||||
|
||||
Where ``<some id>`` would be the primary key of
|
||||
``other``. From the above, it is clear that
|
||||
:meth:`~.RelationshipProperty.Comparator.contains`
|
||||
@@ -597,7 +592,7 @@ class RelationshipProperty(StrategizedProperty):
|
||||
a less-performant alternative using EXISTS, or refer
|
||||
to :meth:`.Query.outerjoin` as well as :ref:`ormtutorial_joins`
|
||||
for more details on constructing outer joins.
|
||||
|
||||
|
||||
"""
|
||||
if not self.property.uselist:
|
||||
raise sa_exc.InvalidRequestError(
|
||||
@@ -635,7 +630,7 @@ class RelationshipProperty(StrategizedProperty):
|
||||
adapt(x) == None)
|
||||
for (x, y) in self.property.local_remote_pairs])
|
||||
|
||||
criterion = sql.and_(*[x==y for (x, y) in
|
||||
criterion = sql.and_(*[x==y for (x, y) in
|
||||
zip(
|
||||
self.property.mapper.primary_key,
|
||||
self.property.\
|
||||
@@ -648,26 +643,26 @@ class RelationshipProperty(StrategizedProperty):
|
||||
"""Implement the ``!=`` operator.
|
||||
|
||||
In a many-to-one context, such as::
|
||||
|
||||
|
||||
MyClass.some_prop != <some object>
|
||||
|
||||
|
||||
This will typically produce a clause such as::
|
||||
|
||||
|
||||
mytable.related_id != <some id>
|
||||
|
||||
|
||||
Where ``<some id>`` is the primary key of the
|
||||
given object.
|
||||
|
||||
|
||||
The ``!=`` operator provides partial functionality for non-
|
||||
many-to-one comparisons:
|
||||
|
||||
|
||||
* Comparisons against collections are not supported.
|
||||
Use
|
||||
:meth:`~.RelationshipProperty.Comparator.contains`
|
||||
in conjunction with :func:`~.expression.not_`.
|
||||
* Compared to a scalar one-to-many, will produce a
|
||||
* Compared to a scalar one-to-many, will produce a
|
||||
clause that compares the target columns in the parent to
|
||||
the given target.
|
||||
the given target.
|
||||
* Compared to a scalar many-to-many, an alias
|
||||
of the association table will be rendered as
|
||||
well, forming a natural join that is part of the
|
||||
@@ -681,7 +676,7 @@ class RelationshipProperty(StrategizedProperty):
|
||||
membership tests.
|
||||
* Comparisons against ``None`` given in a one-to-many
|
||||
or many-to-many context produce an EXISTS clause.
|
||||
|
||||
|
||||
"""
|
||||
if isinstance(other, (NoneType, expression._Null)):
|
||||
if self.property.direction == MANYTOONE:
|
||||
@@ -702,26 +697,26 @@ class RelationshipProperty(StrategizedProperty):
|
||||
configure_mappers()
|
||||
return self.prop
|
||||
|
||||
def compare(self, op, value,
|
||||
value_is_parent=False,
|
||||
def compare(self, op, value,
|
||||
value_is_parent=False,
|
||||
alias_secondary=True):
|
||||
if op == operators.eq:
|
||||
if value is None:
|
||||
if self.uselist:
|
||||
return ~sql.exists([1], self.primaryjoin)
|
||||
else:
|
||||
return self._optimized_compare(None,
|
||||
return self._optimized_compare(None,
|
||||
value_is_parent=value_is_parent,
|
||||
alias_secondary=alias_secondary)
|
||||
else:
|
||||
return self._optimized_compare(value,
|
||||
return self._optimized_compare(value,
|
||||
value_is_parent=value_is_parent,
|
||||
alias_secondary=alias_secondary)
|
||||
else:
|
||||
return op(self.comparator, value)
|
||||
|
||||
def _optimized_compare(self, value, value_is_parent=False,
|
||||
adapt_source=None,
|
||||
def _optimized_compare(self, value, value_is_parent=False,
|
||||
adapt_source=None,
|
||||
alias_secondary=True):
|
||||
if value is not None:
|
||||
value = attributes.instance_state(value)
|
||||
@@ -733,12 +728,12 @@ class RelationshipProperty(StrategizedProperty):
|
||||
def __str__(self):
|
||||
return str(self.parent.class_.__name__) + "." + self.key
|
||||
|
||||
def merge(self,
|
||||
def merge(self,
|
||||
session,
|
||||
source_state,
|
||||
source_dict,
|
||||
dest_state,
|
||||
dest_dict,
|
||||
dest_dict,
|
||||
load, _recursive):
|
||||
|
||||
if load:
|
||||
@@ -848,8 +843,8 @@ class RelationshipProperty(StrategizedProperty):
|
||||
raise AssertionError("Attribute '%s' on class '%s' "
|
||||
"doesn't handle objects "
|
||||
"of type '%s'" % (
|
||||
self.key,
|
||||
self.parent.class_,
|
||||
self.key,
|
||||
self.parent.class_,
|
||||
c.__class__
|
||||
))
|
||||
|
||||
@@ -877,11 +872,11 @@ class RelationshipProperty(StrategizedProperty):
|
||||
|
||||
@util.memoized_property
|
||||
def mapper(self):
|
||||
"""Return the targeted :class:`.Mapper` for this
|
||||
"""Return the targeted :class:`.Mapper` for this
|
||||
:class:`.RelationshipProperty`.
|
||||
|
||||
|
||||
This is a lazy-initializing static attribute.
|
||||
|
||||
|
||||
"""
|
||||
if isinstance(self.argument, type):
|
||||
mapper_ = mapper.class_mapper(self.argument,
|
||||
@@ -905,8 +900,8 @@ class RelationshipProperty(StrategizedProperty):
|
||||
@util.memoized_property
|
||||
@util.deprecated("0.7", "Use .target")
|
||||
def table(self):
|
||||
"""Return the selectable linked to this
|
||||
:class:`.RelationshipProperty` object's target
|
||||
"""Return the selectable linked to this
|
||||
:class:`.RelationshipProperty` object's target
|
||||
:class:`.Mapper`."""
|
||||
return self.target
|
||||
|
||||
@@ -922,7 +917,7 @@ class RelationshipProperty(StrategizedProperty):
|
||||
super(RelationshipProperty, self).do_init()
|
||||
|
||||
def _check_conflicts(self):
|
||||
"""Test that this relationship is legal, warn about
|
||||
"""Test that this relationship is legal, warn about
|
||||
inheritance conflicts."""
|
||||
|
||||
if not self.is_primary() \
|
||||
@@ -949,11 +944,11 @@ class RelationshipProperty(StrategizedProperty):
|
||||
% (self.key, self.parent, inheriting))
|
||||
|
||||
def _process_dependent_arguments(self):
|
||||
"""Convert incoming configuration arguments to their
|
||||
"""Convert incoming configuration arguments to their
|
||||
proper form.
|
||||
|
||||
|
||||
Callables are resolved, ORM annotations removed.
|
||||
|
||||
|
||||
"""
|
||||
# accept callables for other attributes which may require
|
||||
# deferred initialization. This technique is used
|
||||
@@ -983,20 +978,20 @@ class RelationshipProperty(StrategizedProperty):
|
||||
# remote_side are all columns, not strings.
|
||||
if self.order_by is not False and self.order_by is not None:
|
||||
self.order_by = [
|
||||
expression._only_column_elements(x, "order_by")
|
||||
expression._only_column_elements(x, "order_by")
|
||||
for x in
|
||||
util.to_list(self.order_by)]
|
||||
|
||||
self._user_defined_foreign_keys = \
|
||||
util.column_set(
|
||||
expression._only_column_elements(x, "foreign_keys")
|
||||
expression._only_column_elements(x, "foreign_keys")
|
||||
for x in util.to_column_set(
|
||||
self._user_defined_foreign_keys
|
||||
))
|
||||
|
||||
self.remote_side = \
|
||||
util.column_set(
|
||||
expression._only_column_elements(x, "remote_side")
|
||||
expression._only_column_elements(x, "remote_side")
|
||||
for x in
|
||||
util.to_column_set(self.remote_side))
|
||||
|
||||
@@ -1010,10 +1005,10 @@ class RelationshipProperty(StrategizedProperty):
|
||||
def _determine_joins(self):
|
||||
"""Determine the 'primaryjoin' and 'secondaryjoin' attributes,
|
||||
if not passed to the constructor already.
|
||||
|
||||
|
||||
This is based on analysis of the foreign key relationships
|
||||
between the parent and target mapped selectables.
|
||||
|
||||
|
||||
"""
|
||||
if self.secondaryjoin is not None and self.secondary is None:
|
||||
raise sa_exc.ArgumentError("Property '" + self.key
|
||||
@@ -1029,7 +1024,7 @@ class RelationshipProperty(StrategizedProperty):
|
||||
# for more specificity, then if not found will try the more
|
||||
# general mapped table, which in the case of inheritance is
|
||||
# a join.
|
||||
return join_condition(mapper.mapped_table, table,
|
||||
return join_condition(mapper.mapped_table, table,
|
||||
a_subset=mapper.local_table)
|
||||
|
||||
try:
|
||||
@@ -1053,9 +1048,9 @@ class RelationshipProperty(StrategizedProperty):
|
||||
% self)
|
||||
|
||||
def _columns_are_mapped(self, *cols):
|
||||
"""Return True if all columns in the given collection are
|
||||
"""Return True if all columns in the given collection are
|
||||
mapped by the tables referenced by this :class:`.Relationship`.
|
||||
|
||||
|
||||
"""
|
||||
for c in cols:
|
||||
if self.secondary is not None \
|
||||
@@ -1070,11 +1065,11 @@ class RelationshipProperty(StrategizedProperty):
|
||||
"""Determine a list of "source"/"destination" column pairs
|
||||
based on the given join condition, as well as the
|
||||
foreign keys argument.
|
||||
|
||||
|
||||
"source" would be a column referenced by a foreign key,
|
||||
and "destination" would be the column who has a foreign key
|
||||
reference to "source".
|
||||
|
||||
|
||||
"""
|
||||
|
||||
fks = self._user_defined_foreign_keys
|
||||
@@ -1083,7 +1078,7 @@ class RelationshipProperty(StrategizedProperty):
|
||||
consider_as_foreign_keys=fks,
|
||||
any_operator=self.viewonly)
|
||||
|
||||
# couldn't find any fks, but we have
|
||||
# couldn't find any fks, but we have
|
||||
# "secondary" - assume the "secondary" columns
|
||||
# are the fks
|
||||
if not eq_pairs and \
|
||||
@@ -1108,19 +1103,19 @@ class RelationshipProperty(StrategizedProperty):
|
||||
# Filter out just to columns that are mapped.
|
||||
# If viewonly, allow pairs where the FK col
|
||||
# was part of "foreign keys" - the column it references
|
||||
# may be in an un-mapped table - see
|
||||
# may be in an un-mapped table - see
|
||||
# test.orm.test_relationships.ViewOnlyComplexJoin.test_basic
|
||||
# for an example of this.
|
||||
eq_pairs = [(l, r) for (l, r) in eq_pairs
|
||||
if self._columns_are_mapped(l, r)
|
||||
or self.viewonly and
|
||||
or self.viewonly and
|
||||
r in fks]
|
||||
|
||||
if eq_pairs:
|
||||
return eq_pairs
|
||||
|
||||
# from here below is just determining the best error message
|
||||
# to report. Check for a join condition using any operator
|
||||
# to report. Check for a join condition using any operator
|
||||
# (not just ==), perhaps they need to turn on "viewonly=True".
|
||||
if not self.viewonly and criterion_as_pairs(join_condition,
|
||||
consider_as_foreign_keys=self._user_defined_foreign_keys,
|
||||
@@ -1130,8 +1125,8 @@ class RelationshipProperty(StrategizedProperty):
|
||||
"foreign-key-equated, locally mapped column "\
|
||||
"pairs for %s "\
|
||||
"condition '%s' on relationship %s." % (
|
||||
primary and 'primaryjoin' or 'secondaryjoin',
|
||||
join_condition,
|
||||
primary and 'primaryjoin' or 'secondaryjoin',
|
||||
join_condition,
|
||||
self
|
||||
)
|
||||
|
||||
@@ -1160,10 +1155,10 @@ class RelationshipProperty(StrategizedProperty):
|
||||
"have adequate ForeignKey and/or "
|
||||
"ForeignKeyConstraint objects established "
|
||||
"(in which case 'foreign_keys' is usually "
|
||||
"unnecessary)?"
|
||||
"unnecessary)?"
|
||||
% (
|
||||
primary and 'primaryjoin' or 'secondaryjoin',
|
||||
join_condition,
|
||||
join_condition,
|
||||
self,
|
||||
primary and 'mapped' or 'secondary'
|
||||
))
|
||||
@@ -1174,18 +1169,18 @@ class RelationshipProperty(StrategizedProperty):
|
||||
"referencing Column objects have a "
|
||||
"ForeignKey present, or are otherwise part "
|
||||
"of a ForeignKeyConstraint on their parent "
|
||||
"Table, or specify the foreign_keys parameter "
|
||||
"Table, or specify the foreign_keys parameter "
|
||||
"to this relationship."
|
||||
% (
|
||||
primary and 'primaryjoin' or 'secondaryjoin',
|
||||
join_condition,
|
||||
primary and 'primaryjoin' or 'secondaryjoin',
|
||||
join_condition,
|
||||
self
|
||||
))
|
||||
|
||||
def _determine_synchronize_pairs(self):
|
||||
"""Resolve 'primary'/foreign' column pairs from the primaryjoin
|
||||
and secondaryjoin arguments.
|
||||
|
||||
|
||||
"""
|
||||
if self.local_remote_pairs:
|
||||
if not self._user_defined_foreign_keys:
|
||||
@@ -1200,7 +1195,7 @@ class RelationshipProperty(StrategizedProperty):
|
||||
self.synchronize_pairs.append((r, l))
|
||||
else:
|
||||
self.synchronize_pairs = self._sync_pairs_from_join(
|
||||
self.primaryjoin,
|
||||
self.primaryjoin,
|
||||
True)
|
||||
|
||||
self._calculated_foreign_keys = util.column_set(
|
||||
@@ -1209,7 +1204,7 @@ class RelationshipProperty(StrategizedProperty):
|
||||
|
||||
if self.secondaryjoin is not None:
|
||||
self.secondary_synchronize_pairs = self._sync_pairs_from_join(
|
||||
self.secondaryjoin,
|
||||
self.secondaryjoin,
|
||||
False)
|
||||
self._calculated_foreign_keys.update(
|
||||
r for (l, r) in
|
||||
@@ -1218,12 +1213,12 @@ class RelationshipProperty(StrategizedProperty):
|
||||
self.secondary_synchronize_pairs = None
|
||||
|
||||
def _determine_direction(self):
|
||||
"""Determine if this relationship is one to many, many to one,
|
||||
"""Determine if this relationship is one to many, many to one,
|
||||
many to many.
|
||||
|
||||
|
||||
This is derived from the primaryjoin, presence of "secondary",
|
||||
and in the case of self-referential the "remote side".
|
||||
|
||||
|
||||
"""
|
||||
if self.secondaryjoin is not None:
|
||||
self.direction = MANYTOMANY
|
||||
@@ -1300,19 +1295,19 @@ class RelationshipProperty(StrategizedProperty):
|
||||
% self)
|
||||
|
||||
def _determine_local_remote_pairs(self):
|
||||
"""Determine pairs of columns representing "local" to
|
||||
"""Determine pairs of columns representing "local" to
|
||||
"remote", where "local" columns are on the parent mapper,
|
||||
"remote" are on the target mapper.
|
||||
|
||||
|
||||
These pairs are used on the load side only to generate
|
||||
lazy loading clauses.
|
||||
|
||||
"""
|
||||
if not self.local_remote_pairs and not self.remote_side:
|
||||
# the most common, trivial case. Derive
|
||||
# the most common, trivial case. Derive
|
||||
# local/remote pairs from the synchronize pairs.
|
||||
eq_pairs = util.unique_list(
|
||||
self.synchronize_pairs +
|
||||
self.synchronize_pairs +
|
||||
(self.secondary_synchronize_pairs or []))
|
||||
if self.direction is MANYTOONE:
|
||||
self.local_remote_pairs = [(r, l) for l, r in eq_pairs]
|
||||
@@ -1474,8 +1469,8 @@ class RelationshipProperty(StrategizedProperty):
|
||||
if not self.viewonly and self._dependency_processor:
|
||||
self._dependency_processor.per_property_preprocessors(uow)
|
||||
|
||||
def _create_joins(self, source_polymorphic=False,
|
||||
source_selectable=None, dest_polymorphic=False,
|
||||
def _create_joins(self, source_polymorphic=False,
|
||||
source_selectable=None, dest_polymorphic=False,
|
||||
dest_selectable=None, of_type=None):
|
||||
if source_selectable is None:
|
||||
if source_polymorphic and self.parent.with_polymorphic:
|
||||
@@ -1497,10 +1492,10 @@ class RelationshipProperty(StrategizedProperty):
|
||||
|
||||
# place a barrier on the destination such that
|
||||
# replacement traversals won't ever dig into it.
|
||||
# its internal structure remains fixed
|
||||
# its internal structure remains fixed
|
||||
# regardless of context.
|
||||
dest_selectable = _shallow_annotate(
|
||||
dest_selectable,
|
||||
dest_selectable,
|
||||
{'no_replacement_traverse':True})
|
||||
|
||||
aliased = aliased or (source_selectable is not None)
|
||||
|
||||
+398
-343
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
# orm/scoping.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -16,10 +16,10 @@ __all__ = ['ScopedSession']
|
||||
|
||||
class ScopedSession(object):
|
||||
"""Provides thread-local management of Sessions.
|
||||
|
||||
|
||||
Typical invocation is via the :func:`.scoped_session`
|
||||
function::
|
||||
|
||||
|
||||
Session = scoped_session(sessionmaker())
|
||||
|
||||
The internal registry is accessible,
|
||||
@@ -71,7 +71,7 @@ class ScopedSession(object):
|
||||
self.session_factory.configure(**kwargs)
|
||||
|
||||
def query_property(self, query_cls=None):
|
||||
"""return a class property which produces a `Query` object
|
||||
"""return a class property which produces a `Query` object
|
||||
against the class when called.
|
||||
|
||||
e.g.::
|
||||
@@ -122,7 +122,7 @@ def makeprop(name):
|
||||
def get(self):
|
||||
return getattr(self.registry(), name)
|
||||
return property(get, set)
|
||||
for prop in ('bind', 'dirty', 'deleted', 'new', 'identity_map',
|
||||
for prop in ('bind', 'dirty', 'deleted', 'new', 'identity_map',
|
||||
'is_active', 'autoflush', 'no_autoflush'):
|
||||
setattr(ScopedSession, prop, makeprop(prop))
|
||||
|
||||
|
||||
+383
-198
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
# orm/shard.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# orm/state.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -124,7 +124,7 @@ class InstanceState(object):
|
||||
return []
|
||||
elif hasattr(impl, 'get_collection'):
|
||||
return [
|
||||
(attributes.instance_state(o), o) for o in
|
||||
(attributes.instance_state(o), o) for o in
|
||||
impl.get_collection(self, dict_, x, passive=passive)
|
||||
]
|
||||
else:
|
||||
@@ -134,10 +134,10 @@ class InstanceState(object):
|
||||
d = {'instance':self.obj()}
|
||||
d.update(
|
||||
(k, self.__dict__[k]) for k in (
|
||||
'committed_state', 'pending', 'modified', 'expired',
|
||||
'committed_state', 'pending', 'modified', 'expired',
|
||||
'callables', 'key', 'parents', 'load_options', 'mutable_dict',
|
||||
'class_',
|
||||
) if k in self.__dict__
|
||||
) if k in self.__dict__
|
||||
)
|
||||
if self.load_path:
|
||||
d['load_path'] = interfaces.serialize_path(self.load_path)
|
||||
@@ -181,26 +181,26 @@ class InstanceState(object):
|
||||
self.__dict__.update([
|
||||
(k, state[k]) for k in (
|
||||
'key', 'load_options', 'mutable_dict'
|
||||
) if k in state
|
||||
) if k in state
|
||||
])
|
||||
|
||||
if 'load_path' in state:
|
||||
self.load_path = interfaces.deserialize_path(state['load_path'])
|
||||
|
||||
# setup _sa_instance_state ahead of time so that
|
||||
# setup _sa_instance_state ahead of time so that
|
||||
# unpickle events can access the object normally.
|
||||
# see [ticket:2362]
|
||||
manager.setup_instance(inst, self)
|
||||
manager.dispatch.unpickle(self, state)
|
||||
|
||||
def initialize(self, key):
|
||||
"""Set this attribute to an empty value or collection,
|
||||
"""Set this attribute to an empty value or collection,
|
||||
based on the AttributeImpl in use."""
|
||||
|
||||
self.manager.get_impl(key).initialize(self, self.dict)
|
||||
|
||||
def reset(self, dict_, key):
|
||||
"""Remove the given attribute and any
|
||||
"""Remove the given attribute and any
|
||||
callables associated with it."""
|
||||
|
||||
dict_.pop(key, None)
|
||||
@@ -284,7 +284,7 @@ class InstanceState(object):
|
||||
|
||||
self.manager.deferred_scalar_loader(self, toload)
|
||||
|
||||
# if the loader failed, or this
|
||||
# if the loader failed, or this
|
||||
# instance state didn't have an identity,
|
||||
# the attributes still might be in the callables
|
||||
# dict. ensure they are removed.
|
||||
@@ -321,7 +321,7 @@ class InstanceState(object):
|
||||
@property
|
||||
def expired_attributes(self):
|
||||
"""Return the set of keys which are 'expired' to be loaded by
|
||||
the manager's deferred scalar loader, assuming no pending
|
||||
the manager's deferred scalar loader, assuming no pending
|
||||
changes.
|
||||
|
||||
see also the ``unmodified`` collection which is intersected
|
||||
@@ -348,7 +348,7 @@ class InstanceState(object):
|
||||
|
||||
self.committed_state[attr.key] = previous
|
||||
|
||||
# the "or not self.modified" is defensive at
|
||||
# the "or not self.modified" is defensive at
|
||||
# this point. The assertion below is expected
|
||||
# to be True:
|
||||
# assert self._strong_obj is None or self.modified
|
||||
@@ -363,9 +363,9 @@ class InstanceState(object):
|
||||
raise orm_exc.ObjectDereferencedError(
|
||||
"Can't emit change event for attribute '%s' - "
|
||||
"parent object of type %s has been garbage "
|
||||
"collected."
|
||||
"collected."
|
||||
% (
|
||||
self.manager[attr.key],
|
||||
self.manager[attr.key],
|
||||
orm_util.state_class_str(self)
|
||||
))
|
||||
self.modified = True
|
||||
@@ -433,7 +433,7 @@ class InstanceState(object):
|
||||
self._strong_obj = None
|
||||
|
||||
class MutableAttrInstanceState(InstanceState):
|
||||
"""InstanceState implementation for objects that reference 'mutable'
|
||||
"""InstanceState implementation for objects that reference 'mutable'
|
||||
attributes.
|
||||
|
||||
Has a more involved "cleanup" handler that checks mutable attributes
|
||||
@@ -491,7 +491,7 @@ class MutableAttrInstanceState(InstanceState):
|
||||
|
||||
This would be called in the extremely rare
|
||||
race condition that the weakref returned None but
|
||||
the cleanup handler had not yet established the
|
||||
the cleanup handler had not yet established the
|
||||
__resurrect callable as its replacement.
|
||||
|
||||
"""
|
||||
|
||||
+134
-126
@@ -1,10 +1,10 @@
|
||||
# orm/strategies.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""sqlalchemy.orm.interfaces.LoaderStrategy
|
||||
"""sqlalchemy.orm.interfaces.LoaderStrategy
|
||||
implementations, and related MapperOptions."""
|
||||
|
||||
from sqlalchemy import exc as sa_exc
|
||||
@@ -23,15 +23,15 @@ from sqlalchemy.orm.query import Query
|
||||
import itertools
|
||||
|
||||
def _register_attribute(strategy, mapper, useobject,
|
||||
compare_function=None,
|
||||
compare_function=None,
|
||||
typecallable=None,
|
||||
copy_function=None,
|
||||
mutable_scalars=False,
|
||||
copy_function=None,
|
||||
mutable_scalars=False,
|
||||
uselist=False,
|
||||
callable_=None,
|
||||
proxy_property=None,
|
||||
callable_=None,
|
||||
proxy_property=None,
|
||||
active_history=False,
|
||||
impl_class=None,
|
||||
impl_class=None,
|
||||
**kw
|
||||
):
|
||||
|
||||
@@ -45,11 +45,11 @@ def _register_attribute(strategy, mapper, useobject,
|
||||
listen_hooks.append(single_parent_validator)
|
||||
|
||||
if prop.key in prop.parent.validators:
|
||||
fn, include_removes = prop.parent.validators[prop.key]
|
||||
listen_hooks.append(
|
||||
lambda desc, prop: mapperutil._validator_events(desc,
|
||||
prop.key,
|
||||
prop.parent.validators[prop.key])
|
||||
)
|
||||
lambda desc, prop: mapperutil._validator_events(desc,
|
||||
prop.key, fn, include_removes)
|
||||
)
|
||||
|
||||
if useobject:
|
||||
listen_hooks.append(unitofwork.track_cascade_events)
|
||||
@@ -59,8 +59,8 @@ def _register_attribute(strategy, mapper, useobject,
|
||||
backref = kw.pop('backref', None)
|
||||
if backref:
|
||||
listen_hooks.append(
|
||||
lambda desc, prop: attributes.backref_listeners(desc,
|
||||
backref,
|
||||
lambda desc, prop: attributes.backref_listeners(desc,
|
||||
backref,
|
||||
uselist)
|
||||
)
|
||||
|
||||
@@ -68,18 +68,18 @@ def _register_attribute(strategy, mapper, useobject,
|
||||
if prop is m._props.get(prop.key):
|
||||
|
||||
desc = attributes.register_attribute_impl(
|
||||
m.class_,
|
||||
prop.key,
|
||||
m.class_,
|
||||
prop.key,
|
||||
parent_token=prop,
|
||||
mutable_scalars=mutable_scalars,
|
||||
uselist=uselist,
|
||||
copy_function=copy_function,
|
||||
compare_function=compare_function,
|
||||
uselist=uselist,
|
||||
copy_function=copy_function,
|
||||
compare_function=compare_function,
|
||||
useobject=useobject,
|
||||
extension=attribute_ext,
|
||||
trackparent=useobject and (prop.single_parent or prop.direction is interfaces.ONETOMANY),
|
||||
extension=attribute_ext,
|
||||
trackparent=useobject and (prop.single_parent or prop.direction is interfaces.ONETOMANY),
|
||||
typecallable=typecallable,
|
||||
callable_=callable_,
|
||||
callable_=callable_,
|
||||
active_history=active_history,
|
||||
impl_class=impl_class,
|
||||
doc=prop.doc,
|
||||
@@ -99,7 +99,7 @@ class UninstrumentedColumnLoader(LoaderStrategy):
|
||||
def init(self):
|
||||
self.columns = self.parent_property.columns
|
||||
|
||||
def setup_query(self, context, entity, path, reduced_path, adapter,
|
||||
def setup_query(self, context, entity, path, reduced_path, adapter,
|
||||
column_collection=None, **kwargs):
|
||||
for c in self.columns:
|
||||
if adapter:
|
||||
@@ -116,7 +116,7 @@ class ColumnLoader(LoaderStrategy):
|
||||
self.columns = self.parent_property.columns
|
||||
self.is_composite = hasattr(self.parent_property, 'composite_class')
|
||||
|
||||
def setup_query(self, context, entity, path, reduced_path,
|
||||
def setup_query(self, context, entity, path, reduced_path,
|
||||
adapter, column_collection, **kwargs):
|
||||
for c in self.columns:
|
||||
if adapter:
|
||||
@@ -137,7 +137,7 @@ class ColumnLoader(LoaderStrategy):
|
||||
active_history = active_history
|
||||
)
|
||||
|
||||
def create_row_processor(self, context, path, reduced_path,
|
||||
def create_row_processor(self, context, path, reduced_path,
|
||||
mapper, row, adapter):
|
||||
key = self.key
|
||||
# look through list of columns represented here
|
||||
@@ -199,10 +199,10 @@ class DeferredColumnLoader(LoaderStrategy):
|
||||
expire_missing=False
|
||||
)
|
||||
|
||||
def setup_query(self, context, entity, path, reduced_path, adapter,
|
||||
def setup_query(self, context, entity, path, reduced_path, adapter,
|
||||
only_load_props=None, **kwargs):
|
||||
if (
|
||||
self.group is not None and
|
||||
self.group is not None and
|
||||
context.attributes.get(('undefer', self.group), False)
|
||||
) or (only_load_props and self.key in only_load_props):
|
||||
self.parent_property._get_strategy(ColumnLoader).\
|
||||
@@ -220,10 +220,10 @@ class DeferredColumnLoader(LoaderStrategy):
|
||||
|
||||
if self.group:
|
||||
toload = [
|
||||
p.key for p in
|
||||
localparent.iterate_properties
|
||||
if isinstance(p, StrategizedProperty) and
|
||||
isinstance(p.strategy, DeferredColumnLoader) and
|
||||
p.key for p in
|
||||
localparent.iterate_properties
|
||||
if isinstance(p, StrategizedProperty) and
|
||||
isinstance(p.strategy, DeferredColumnLoader) and
|
||||
p.group==self.group
|
||||
]
|
||||
else:
|
||||
@@ -236,12 +236,12 @@ class DeferredColumnLoader(LoaderStrategy):
|
||||
if session is None:
|
||||
raise orm_exc.DetachedInstanceError(
|
||||
"Parent instance %s is not bound to a Session; "
|
||||
"deferred load operation of attribute '%s' cannot proceed" %
|
||||
"deferred load operation of attribute '%s' cannot proceed" %
|
||||
(mapperutil.state_str(state), self.key)
|
||||
)
|
||||
|
||||
query = session.query(localparent)
|
||||
if query._load_on_ident(state.key,
|
||||
if query._load_on_ident(state.key,
|
||||
only_load_props=group, refresh_state=state) is None:
|
||||
raise orm_exc.ObjectDeletedError(state)
|
||||
|
||||
@@ -297,14 +297,14 @@ class AbstractRelationshipLoader(LoaderStrategy):
|
||||
class NoLoader(AbstractRelationshipLoader):
|
||||
"""Provide loading behavior for a :class:`.RelationshipProperty`
|
||||
with "lazy=None".
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def init_class_attribute(self, mapper):
|
||||
self.is_class_level = True
|
||||
|
||||
_register_attribute(self, mapper,
|
||||
useobject=True,
|
||||
useobject=True,
|
||||
uselist=self.parent_property.uselist,
|
||||
typecallable = self.parent_property.collection_class,
|
||||
)
|
||||
@@ -319,7 +319,7 @@ log.class_logger(NoLoader)
|
||||
class LazyLoader(AbstractRelationshipLoader):
|
||||
"""Provide loading behavior for a :class:`.RelationshipProperty`
|
||||
with "lazy=True", that is loads when first accessed.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def init(self):
|
||||
@@ -331,7 +331,7 @@ class LazyLoader(AbstractRelationshipLoader):
|
||||
self._rev_lazywhere, \
|
||||
self._rev_bind_to_col, \
|
||||
self._rev_equated_columns = self._create_lazy_clause(
|
||||
self.parent_property,
|
||||
self.parent_property,
|
||||
reverse_direction=True)
|
||||
|
||||
self.logger.info("%s lazy loading clause %s", self, self._lazywhere)
|
||||
@@ -341,8 +341,8 @@ class LazyLoader(AbstractRelationshipLoader):
|
||||
#from sqlalchemy.orm import query
|
||||
self.use_get = not self.uselist and \
|
||||
self.mapper._get_clause[0].compare(
|
||||
self._lazywhere,
|
||||
use_proxies=True,
|
||||
self._lazywhere,
|
||||
use_proxies=True,
|
||||
equivalents=self.mapper._equivalent_columns
|
||||
)
|
||||
|
||||
@@ -358,13 +358,13 @@ class LazyLoader(AbstractRelationshipLoader):
|
||||
def init_class_attribute(self, mapper):
|
||||
self.is_class_level = True
|
||||
|
||||
# MANYTOONE currently only needs the
|
||||
# MANYTOONE currently only needs the
|
||||
# "old" value for delete-orphan
|
||||
# cascades. the required _SingleParentValidator
|
||||
# cascades. the required _SingleParentValidator
|
||||
# will enable active_history
|
||||
# in that case. otherwise we don't need the
|
||||
# in that case. otherwise we don't need the
|
||||
# "old" value during backref operations.
|
||||
_register_attribute(self,
|
||||
_register_attribute(self,
|
||||
mapper,
|
||||
useobject=True,
|
||||
callable_=self._load_for_state,
|
||||
@@ -378,12 +378,12 @@ class LazyLoader(AbstractRelationshipLoader):
|
||||
not self.use_get,
|
||||
)
|
||||
|
||||
def lazy_clause(self, state, reverse_direction=False,
|
||||
alias_secondary=False,
|
||||
def lazy_clause(self, state, reverse_direction=False,
|
||||
alias_secondary=False,
|
||||
adapt_source=None):
|
||||
if state is None:
|
||||
return self._lazy_none_clause(
|
||||
reverse_direction,
|
||||
reverse_direction,
|
||||
adapt_source=adapt_source)
|
||||
|
||||
if not reverse_direction:
|
||||
@@ -414,14 +414,14 @@ class LazyLoader(AbstractRelationshipLoader):
|
||||
if bindparam._identifying_key in bind_to_col:
|
||||
bindparam.callable = \
|
||||
lambda: mapper._get_committed_state_attr_by_column(
|
||||
state, dict_,
|
||||
state, dict_,
|
||||
bind_to_col[bindparam._identifying_key])
|
||||
else:
|
||||
def visit_bindparam(bindparam):
|
||||
if bindparam._identifying_key in bind_to_col:
|
||||
bindparam.callable = \
|
||||
lambda: mapper._get_state_attr_by_column(
|
||||
state, dict_,
|
||||
state, dict_,
|
||||
bind_to_col[bindparam._identifying_key])
|
||||
|
||||
|
||||
@@ -465,10 +465,10 @@ class LazyLoader(AbstractRelationshipLoader):
|
||||
|
||||
if (
|
||||
(passive is attributes.PASSIVE_NO_FETCH or \
|
||||
passive is attributes.PASSIVE_NO_FETCH_RELATED) and
|
||||
passive is attributes.PASSIVE_NO_FETCH_RELATED) and
|
||||
not self.use_get
|
||||
) or (
|
||||
passive is attributes.PASSIVE_ONLY_PERSISTENT and
|
||||
passive is attributes.PASSIVE_ONLY_PERSISTENT and
|
||||
pending
|
||||
):
|
||||
return attributes.PASSIVE_NO_RESULT
|
||||
@@ -477,11 +477,11 @@ class LazyLoader(AbstractRelationshipLoader):
|
||||
if not session:
|
||||
raise orm_exc.DetachedInstanceError(
|
||||
"Parent instance %s is not bound to a Session; "
|
||||
"lazy load operation of attribute '%s' cannot proceed" %
|
||||
"lazy load operation of attribute '%s' cannot proceed" %
|
||||
(mapperutil.state_str(state), self.key)
|
||||
)
|
||||
|
||||
# if we have a simple primary key load, check the
|
||||
# if we have a simple primary key load, check the
|
||||
# identity map without generating a Query at all
|
||||
if self.use_get:
|
||||
ident = self._get_ident_for_use_get(
|
||||
@@ -555,7 +555,7 @@ class LazyLoader(AbstractRelationshipLoader):
|
||||
q = q.order_by(*util.to_list(self.parent_property.order_by))
|
||||
|
||||
for rev in self.parent_property._reverse_property:
|
||||
# reverse props that are MANYTOONE are loading *this*
|
||||
# reverse props that are MANYTOONE are loading *this*
|
||||
# object from get(), so don't need to eager out to those.
|
||||
if rev.direction is interfaces.MANYTOONE and \
|
||||
rev._use_get and \
|
||||
@@ -580,7 +580,7 @@ class LazyLoader(AbstractRelationshipLoader):
|
||||
if l > 1:
|
||||
util.warn(
|
||||
"Multiple rows returned with "
|
||||
"uselist=False for lazily-loaded attribute '%s' "
|
||||
"uselist=False for lazily-loaded attribute '%s' "
|
||||
% self.parent_property)
|
||||
|
||||
return result[0]
|
||||
@@ -588,30 +588,30 @@ class LazyLoader(AbstractRelationshipLoader):
|
||||
return None
|
||||
|
||||
|
||||
def create_row_processor(self, context, path, reduced_path,
|
||||
def create_row_processor(self, context, path, reduced_path,
|
||||
mapper, row, adapter):
|
||||
key = self.key
|
||||
if not self.is_class_level:
|
||||
def set_lazy_callable(state, dict_, row):
|
||||
# we are not the primary manager for this attribute
|
||||
# we are not the primary manager for this attribute
|
||||
# on this class - set up a
|
||||
# per-instance lazyloader, which will override the
|
||||
# per-instance lazyloader, which will override the
|
||||
# class-level behavior.
|
||||
# this currently only happens when using a
|
||||
# this currently only happens when using a
|
||||
# "lazyload" option on a "no load"
|
||||
# attribute - "eager" attributes always have a
|
||||
# attribute - "eager" attributes always have a
|
||||
# class-level lazyloader installed.
|
||||
state.set_callable(dict_, key, LoadLazyAttribute(state, key))
|
||||
return set_lazy_callable, None, None
|
||||
else:
|
||||
def reset_for_lazy_callable(state, dict_, row):
|
||||
# we are the primary manager for this attribute on
|
||||
# we are the primary manager for this attribute on
|
||||
# this class - reset its
|
||||
# per-instance attribute state, so that the class-level
|
||||
# per-instance attribute state, so that the class-level
|
||||
# lazy loader is
|
||||
# executed when next referenced on this instance.
|
||||
# this is needed in
|
||||
# populate_existing() types of scenarios to reset
|
||||
# populate_existing() types of scenarios to reset
|
||||
# any existing state.
|
||||
state.reset(dict_, key)
|
||||
|
||||
@@ -648,7 +648,7 @@ class LazyLoader(AbstractRelationshipLoader):
|
||||
|
||||
if prop.secondaryjoin is None or not reverse_direction:
|
||||
lazywhere = visitors.replacement_traverse(
|
||||
lazywhere, {}, col_to_bind)
|
||||
lazywhere, {}, col_to_bind)
|
||||
|
||||
if prop.secondaryjoin is not None:
|
||||
secondaryjoin = prop.secondaryjoin
|
||||
@@ -685,12 +685,12 @@ class ImmediateLoader(AbstractRelationshipLoader):
|
||||
_get_strategy(LazyLoader).\
|
||||
init_class_attribute(mapper)
|
||||
|
||||
def setup_query(self, context, entity,
|
||||
def setup_query(self, context, entity,
|
||||
path, reduced_path, adapter, column_collection=None,
|
||||
parentmapper=None, **kwargs):
|
||||
pass
|
||||
|
||||
def create_row_processor(self, context, path, reduced_path,
|
||||
def create_row_processor(self, context, path, reduced_path,
|
||||
mapper, row, adapter):
|
||||
def load_immediate(state, dict_, row):
|
||||
state.get_impl(self.key).get(state, dict_)
|
||||
@@ -707,8 +707,8 @@ class SubqueryLoader(AbstractRelationshipLoader):
|
||||
_get_strategy(LazyLoader).\
|
||||
init_class_attribute(mapper)
|
||||
|
||||
def setup_query(self, context, entity,
|
||||
path, reduced_path, adapter,
|
||||
def setup_query(self, context, entity,
|
||||
path, reduced_path, adapter,
|
||||
column_collection=None,
|
||||
parentmapper=None, **kwargs):
|
||||
|
||||
@@ -738,7 +738,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
|
||||
self._get_leftmost(subq_path)
|
||||
|
||||
orig_query = context.attributes.get(
|
||||
("orig_query", SubqueryLoader),
|
||||
("orig_query", SubqueryLoader),
|
||||
context.query)
|
||||
|
||||
# generate a new Query from the original, then
|
||||
@@ -748,7 +748,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
|
||||
leftmost_attr, subq_path
|
||||
)
|
||||
|
||||
# generate another Query that will join the
|
||||
# generate another Query that will join the
|
||||
# left alias to the target relationships.
|
||||
# basically doing a longhand
|
||||
# "from_self()". (from_self() itself not quite industrial
|
||||
@@ -770,7 +770,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
|
||||
q = self._setup_options(q, subq_path, orig_query)
|
||||
q = self._setup_outermost_orderby(q)
|
||||
|
||||
# add new query to attributes to be picked up
|
||||
# add new query to attributes to be picked up
|
||||
# by create_row_processor
|
||||
context.attributes[('subquery', reduced_path)] = q
|
||||
|
||||
@@ -801,7 +801,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
|
||||
# to look only for significant columns
|
||||
q = orig_query._clone()
|
||||
|
||||
# TODO: why does polymporphic etc. require hardcoding
|
||||
# TODO: why does polymporphic etc. require hardcoding
|
||||
# into _adapt_col_list ? Does query.add_columns(...) work
|
||||
# with polymorphic loading ?
|
||||
q._set_entities(q._adapt_col_list(leftmost_attr))
|
||||
@@ -823,7 +823,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
|
||||
def _prep_for_joins(self, left_alias, subq_path):
|
||||
# figure out what's being joined. a.k.a. the fun part
|
||||
to_join = [
|
||||
(subq_path[i], subq_path[i+1])
|
||||
(subq_path[i], subq_path[i+1])
|
||||
for i in xrange(0, len(subq_path), 2)
|
||||
]
|
||||
|
||||
@@ -836,13 +836,13 @@ class SubqueryLoader(AbstractRelationshipLoader):
|
||||
parent_alias = left_alias
|
||||
elif subq_path[-2].isa(self.parent):
|
||||
# In the case of multiple levels, retrieve
|
||||
# it from subq_path[-2]. This is the same as self.parent
|
||||
# in the vast majority of cases, and [ticket:2014]
|
||||
# it from subq_path[-2]. This is the same as self.parent
|
||||
# in the vast majority of cases, and [ticket:2014]
|
||||
# illustrates a case where sub_path[-2] is a subclass
|
||||
# of self.parent
|
||||
parent_alias = mapperutil.AliasedClass(subq_path[-2])
|
||||
else:
|
||||
# if of_type() were used leading to this relationship,
|
||||
# if of_type() were used leading to this relationship,
|
||||
# self.parent is more specific than subq_path[-2]
|
||||
parent_alias = mapperutil.AliasedClass(self.parent)
|
||||
|
||||
@@ -860,10 +860,10 @@ class SubqueryLoader(AbstractRelationshipLoader):
|
||||
for i, (mapper, key) in enumerate(to_join):
|
||||
|
||||
# we need to use query.join() as opposed to
|
||||
# orm.join() here because of the
|
||||
# rich behavior it brings when dealing with
|
||||
# orm.join() here because of the
|
||||
# rich behavior it brings when dealing with
|
||||
# "with_polymorphic" mappers. "aliased"
|
||||
# and "from_joinpoint" take care of most of
|
||||
# and "from_joinpoint" take care of most of
|
||||
# the chaining and aliasing for us.
|
||||
|
||||
first = i == 0
|
||||
@@ -897,12 +897,14 @@ class SubqueryLoader(AbstractRelationshipLoader):
|
||||
# these will fire relative to subq_path.
|
||||
q = q._with_current_path(subq_path)
|
||||
q = q._conditional_options(*orig_query._with_options)
|
||||
if orig_query._populate_existing:
|
||||
q._populate_existing = orig_query._populate_existing
|
||||
return q
|
||||
|
||||
def _setup_outermost_orderby(self, q):
|
||||
if self.parent_property.order_by:
|
||||
# if there's an ORDER BY, alias it the same
|
||||
# way joinedloader does, but we have to pull out
|
||||
# way joinedloader does, but we have to pull out
|
||||
# the "eagerjoin" from the query.
|
||||
# this really only picks up the "secondary" table
|
||||
# right now.
|
||||
@@ -917,12 +919,12 @@ class SubqueryLoader(AbstractRelationshipLoader):
|
||||
q = q.order_by(*eager_order_by)
|
||||
return q
|
||||
|
||||
def create_row_processor(self, context, path, reduced_path,
|
||||
def create_row_processor(self, context, path, reduced_path,
|
||||
mapper, row, adapter):
|
||||
if not self.parent.class_manager[self.key].impl.supports_population:
|
||||
raise sa_exc.InvalidRequestError(
|
||||
"'%s' does not support object "
|
||||
"population - eager loading cannot be applied." %
|
||||
"population - eager loading cannot be applied." %
|
||||
self)
|
||||
|
||||
reduced_path = reduced_path + (self.key,)
|
||||
@@ -934,10 +936,16 @@ class SubqueryLoader(AbstractRelationshipLoader):
|
||||
|
||||
q = context.attributes[('subquery', reduced_path)]
|
||||
|
||||
collections = dict(
|
||||
(k, [v[0] for v in v])
|
||||
# cache the loaded collections in the context
|
||||
# so that inheriting mappers don't re-load when they
|
||||
# call upon create_row_processor again
|
||||
if ('collections', reduced_path) in context.attributes:
|
||||
collections = context.attributes[('collections', reduced_path)]
|
||||
else:
|
||||
collections = context.attributes[('collections', reduced_path)] = dict(
|
||||
(k, [v[0] for v in v])
|
||||
for k, v in itertools.groupby(
|
||||
q,
|
||||
q,
|
||||
lambda x:x[1:]
|
||||
))
|
||||
|
||||
@@ -952,7 +960,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
|
||||
def _create_collection_loader(self, collections, local_cols):
|
||||
def load_collection_from_subq(state, dict_, row):
|
||||
collection = collections.get(
|
||||
tuple([row[col] for col in local_cols]),
|
||||
tuple([row[col] for col in local_cols]),
|
||||
()
|
||||
)
|
||||
state.get_impl(self.key).\
|
||||
@@ -963,7 +971,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
|
||||
def _create_scalar_loader(self, collections, local_cols):
|
||||
def load_scalar_from_subq(state, dict_, row):
|
||||
collection = collections.get(
|
||||
tuple([row[col] for col in local_cols]),
|
||||
tuple([row[col] for col in local_cols]),
|
||||
(None,)
|
||||
)
|
||||
if len(collection) > 1:
|
||||
@@ -983,7 +991,7 @@ log.class_logger(SubqueryLoader)
|
||||
class JoinedLoader(AbstractRelationshipLoader):
|
||||
"""Provide loading behavior for a :class:`.RelationshipProperty`
|
||||
using joined eager loading.
|
||||
|
||||
|
||||
"""
|
||||
def init(self):
|
||||
super(JoinedLoader, self).init()
|
||||
@@ -1014,7 +1022,7 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
)
|
||||
else:
|
||||
# check for join_depth or basic recursion,
|
||||
# if the current path was not explicitly stated as
|
||||
# if the current path was not explicitly stated as
|
||||
# a desired "loaderstrategy" (i.e. via query.options())
|
||||
if ("loaderstrategy", reduced_path) not in context.attributes:
|
||||
if self.join_depth:
|
||||
@@ -1035,16 +1043,16 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
|
||||
for value in self.mapper._polymorphic_properties:
|
||||
value.setup(
|
||||
context,
|
||||
entity,
|
||||
path,
|
||||
context,
|
||||
entity,
|
||||
path,
|
||||
reduced_path,
|
||||
clauses,
|
||||
parentmapper=self.mapper,
|
||||
clauses,
|
||||
parentmapper=self.mapper,
|
||||
column_collection=add_to_collection,
|
||||
allow_innerjoin=allow_innerjoin)
|
||||
|
||||
def _get_user_defined_adapter(self, context, entity,
|
||||
def _get_user_defined_adapter(self, context, entity,
|
||||
reduced_path, adapter):
|
||||
clauses = context.attributes[
|
||||
("user_defined_eager_row_processor",
|
||||
@@ -1063,12 +1071,12 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
add_to_collection = context.primary_columns
|
||||
return clauses, adapter, add_to_collection
|
||||
|
||||
def _generate_row_adapter(self,
|
||||
def _generate_row_adapter(self,
|
||||
context, entity, path, reduced_path, adapter,
|
||||
column_collection, parentmapper, allow_innerjoin
|
||||
):
|
||||
clauses = mapperutil.ORMAdapter(
|
||||
mapperutil.AliasedClass(self.mapper),
|
||||
mapperutil.AliasedClass(self.mapper),
|
||||
equivalents=self.mapper._equivalent_columns,
|
||||
adapt_required=True)
|
||||
|
||||
@@ -1076,7 +1084,7 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
context.multi_row_eager_loaders = True
|
||||
|
||||
innerjoin = allow_innerjoin and context.attributes.get(
|
||||
("eager_join_type", path),
|
||||
("eager_join_type", path),
|
||||
self.parent_property.innerjoin)
|
||||
if not innerjoin:
|
||||
# if this is an outer join, all eager joins from
|
||||
@@ -1084,8 +1092,8 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
allow_innerjoin = False
|
||||
|
||||
context.create_eager_joins.append(
|
||||
(self._create_eager_join, context,
|
||||
entity, path, adapter,
|
||||
(self._create_eager_join, context,
|
||||
entity, path, adapter,
|
||||
parentmapper, clauses, innerjoin)
|
||||
)
|
||||
|
||||
@@ -1095,8 +1103,8 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
] = clauses
|
||||
return clauses, adapter, add_to_collection, allow_innerjoin
|
||||
|
||||
def _create_eager_join(self, context, entity,
|
||||
path, adapter, parentmapper,
|
||||
def _create_eager_join(self, context, entity,
|
||||
path, adapter, parentmapper,
|
||||
clauses, innerjoin):
|
||||
|
||||
if parentmapper is None:
|
||||
@@ -1105,7 +1113,7 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
localparent = parentmapper
|
||||
|
||||
# whether or not the Query will wrap the selectable in a subquery,
|
||||
# and then attach eager load joins to that (i.e., in the case of
|
||||
# and then attach eager load joins to that (i.e., in the case of
|
||||
# LIMIT/OFFSET etc.)
|
||||
should_nest_selectable = context.multi_row_eager_loaders and \
|
||||
context.query._should_nest_selectable
|
||||
@@ -1120,7 +1128,7 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
if clause is not None:
|
||||
# join to an existing FROM clause on the query.
|
||||
# key it to its list index in the eager_joins dict.
|
||||
# Query._compile_context will adapt as needed and
|
||||
# Query._compile_context will adapt as needed and
|
||||
# append to the FROM clause of the select().
|
||||
entity_key, default_towrap = index, clause
|
||||
|
||||
@@ -1138,14 +1146,14 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
else:
|
||||
onclause = getattr(
|
||||
mapperutil.AliasedClass(
|
||||
self.parent,
|
||||
self.parent,
|
||||
adapter.selectable
|
||||
),
|
||||
),
|
||||
self.key, self.parent_property
|
||||
)
|
||||
|
||||
if onclause is self.parent_property:
|
||||
# TODO: this is a temporary hack to
|
||||
# TODO: this is a temporary hack to
|
||||
# account for polymorphic eager loads where
|
||||
# the eagerload is referencing via of_type().
|
||||
join_to_left = True
|
||||
@@ -1154,10 +1162,10 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
|
||||
context.eager_joins[entity_key] = eagerjoin = \
|
||||
mapperutil.join(
|
||||
towrap,
|
||||
clauses.aliased_class,
|
||||
onclause,
|
||||
join_to_left=join_to_left,
|
||||
towrap,
|
||||
clauses.aliased_class,
|
||||
onclause,
|
||||
join_to_left=join_to_left,
|
||||
isouter=not innerjoin
|
||||
)
|
||||
|
||||
@@ -1167,11 +1175,11 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
if self.parent_property.secondary is None and \
|
||||
not parentmapper:
|
||||
# for parentclause that is the non-eager end of the join,
|
||||
# ensure all the parent cols in the primaryjoin are actually
|
||||
# ensure all the parent cols in the primaryjoin are actually
|
||||
# in the
|
||||
# columns clause (i.e. are not deferred), so that aliasing applied
|
||||
# columns clause (i.e. are not deferred), so that aliasing applied
|
||||
# by the Query propagates those columns outward.
|
||||
# This has the effect
|
||||
# This has the effect
|
||||
# of "undefering" those columns.
|
||||
for col in sql_util.find_columns(
|
||||
self.parent_property.primaryjoin):
|
||||
@@ -1196,7 +1204,7 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
decorator = context.attributes[
|
||||
("user_defined_eager_row_processor",
|
||||
reduced_path)]
|
||||
# user defined eagerloads are part of the "primary"
|
||||
# user defined eagerloads are part of the "primary"
|
||||
# portion of the load.
|
||||
# the adapters applied to the Query should be honored.
|
||||
if context.adapter and decorator:
|
||||
@@ -1213,7 +1221,7 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
self.mapper.identity_key_from_row(row, decorator)
|
||||
return decorator
|
||||
except KeyError:
|
||||
# no identity key - dont return a row
|
||||
# no identity key - dont return a row
|
||||
# processor, will cause a degrade to lazy
|
||||
return False
|
||||
|
||||
@@ -1221,23 +1229,23 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
if not self.parent.class_manager[self.key].impl.supports_population:
|
||||
raise sa_exc.InvalidRequestError(
|
||||
"'%s' does not support object "
|
||||
"population - eager loading cannot be applied." %
|
||||
"population - eager loading cannot be applied." %
|
||||
self)
|
||||
|
||||
our_path = path + (self.key,)
|
||||
our_reduced_path = reduced_path + (self.key,)
|
||||
|
||||
eager_adapter = self._create_eager_adapter(
|
||||
context,
|
||||
row,
|
||||
context,
|
||||
row,
|
||||
adapter, our_path,
|
||||
our_reduced_path)
|
||||
|
||||
if eager_adapter is not False:
|
||||
key = self.key
|
||||
_instance = self.mapper._instance_processor(
|
||||
context,
|
||||
our_path + (self.mapper,),
|
||||
context,
|
||||
our_path + (self.mapper,),
|
||||
our_reduced_path + (self.mapper.base_mapper,),
|
||||
eager_adapter)
|
||||
|
||||
@@ -1249,7 +1257,7 @@ class JoinedLoader(AbstractRelationshipLoader):
|
||||
return self.parent_property.\
|
||||
_get_strategy(LazyLoader).\
|
||||
create_row_processor(
|
||||
context, path,
|
||||
context, path,
|
||||
reduced_path,
|
||||
mapper, row, adapter)
|
||||
|
||||
@@ -1380,7 +1388,7 @@ class LoadEagerFromAliasOption(PropertyOption):
|
||||
prop = root_mapper._props[propname]
|
||||
adapter = query._polymorphic_adapters.get(prop.mapper, None)
|
||||
query._attributes.setdefault(
|
||||
("user_defined_eager_row_processor",
|
||||
("user_defined_eager_row_processor",
|
||||
interfaces._reduce_path(path)), adapter)
|
||||
|
||||
if self.alias is not None:
|
||||
@@ -1389,7 +1397,7 @@ class LoadEagerFromAliasOption(PropertyOption):
|
||||
prop = root_mapper._props[propname]
|
||||
self.alias = prop.target.alias(self.alias)
|
||||
query._attributes[
|
||||
("user_defined_eager_row_processor",
|
||||
("user_defined_eager_row_processor",
|
||||
interfaces._reduce_path(paths[-1]))
|
||||
] = sql_util.ColumnAdapter(self.alias)
|
||||
else:
|
||||
@@ -1397,18 +1405,18 @@ class LoadEagerFromAliasOption(PropertyOption):
|
||||
prop = root_mapper._props[propname]
|
||||
adapter = query._polymorphic_adapters.get(prop.mapper, None)
|
||||
query._attributes[
|
||||
("user_defined_eager_row_processor",
|
||||
("user_defined_eager_row_processor",
|
||||
interfaces._reduce_path(paths[-1]))] = adapter
|
||||
|
||||
def single_parent_validator(desc, prop):
|
||||
def _do_check(state, value, oldvalue, initiator):
|
||||
if value is not None and initiator.key == prop.key:
|
||||
hasparent = initiator.hasparent(attributes.instance_state(value))
|
||||
if hasparent and oldvalue is not value:
|
||||
if hasparent and oldvalue is not value:
|
||||
raise sa_exc.InvalidRequestError(
|
||||
"Instance %s is already associated with an instance "
|
||||
"of %s via its %s attribute, and is only allowed a "
|
||||
"single parent." %
|
||||
"single parent." %
|
||||
(mapperutil.instance_str(value), state.class_, prop)
|
||||
)
|
||||
return value
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# orm/sync.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""private module containing functions used for copying data
|
||||
"""private module containing functions used for copying data
|
||||
between instances based on join conditions.
|
||||
|
||||
"""
|
||||
|
||||
from sqlalchemy.orm import exc, util as mapperutil, attributes
|
||||
|
||||
def populate(source, source_mapper, dest, dest_mapper,
|
||||
def populate(source, source_mapper, dest, dest_mapper,
|
||||
synchronize_pairs, uowcommit, flag_cascaded_pks):
|
||||
source_dict = source.dict
|
||||
dest_dict = dest.dict
|
||||
@@ -20,7 +20,7 @@ def populate(source, source_mapper, dest, dest_mapper,
|
||||
try:
|
||||
# inline of source_mapper._get_state_attr_by_column
|
||||
prop = source_mapper._columntoproperty[l]
|
||||
value = source.manager[prop.key].impl.get(source, source_dict,
|
||||
value = source.manager[prop.key].impl.get(source, source_dict,
|
||||
attributes.PASSIVE_OFF)
|
||||
except exc.UnmappedColumnError:
|
||||
_raise_col_to_prop(False, source_mapper, l, dest_mapper, r)
|
||||
@@ -47,7 +47,7 @@ def clear(dest, dest_mapper, synchronize_pairs):
|
||||
if r.primary_key:
|
||||
raise AssertionError(
|
||||
"Dependency rule tried to blank-out primary key "
|
||||
"column '%s' on instance '%s'" %
|
||||
"column '%s' on instance '%s'" %
|
||||
(r, mapperutil.state_str(dest))
|
||||
)
|
||||
try:
|
||||
@@ -75,7 +75,7 @@ def populate_dict(source, source_mapper, dict_, synchronize_pairs):
|
||||
dict_[r.key] = value
|
||||
|
||||
def source_modified(uowcommit, source, source_mapper, synchronize_pairs):
|
||||
"""return true if the source object has changes from an old to a
|
||||
"""return true if the source object has changes from an old to a
|
||||
new value on the given synchronize pairs
|
||||
|
||||
"""
|
||||
@@ -84,7 +84,7 @@ def source_modified(uowcommit, source, source_mapper, synchronize_pairs):
|
||||
prop = source_mapper._columntoproperty[l]
|
||||
except exc.UnmappedColumnError:
|
||||
_raise_col_to_prop(False, source_mapper, l, None, r)
|
||||
history = uowcommit.get_attribute_history(source, prop.key,
|
||||
history = uowcommit.get_attribute_history(source, prop.key,
|
||||
attributes.PASSIVE_NO_INITIALIZE)
|
||||
return bool(history.deleted)
|
||||
else:
|
||||
@@ -103,6 +103,6 @@ def _raise_col_to_prop(isdest, source_mapper, source_column, dest_mapper, dest_c
|
||||
"Can't execute sync rule for source column '%s'; mapper '%s' "
|
||||
"does not map this column. Try using an explicit `foreign_keys`"
|
||||
" collection which does not include destination column '%s' (or "
|
||||
"use a viewonly=True relation)." %
|
||||
"use a viewonly=True relation)." %
|
||||
(source_column, source_mapper, dest_column)
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# orm/unitofwork.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -26,7 +26,7 @@ def track_cascade_events(descriptor, prop):
|
||||
key = prop.key
|
||||
|
||||
def append(state, item, initiator):
|
||||
# process "save_update" cascade rules for when
|
||||
# process "save_update" cascade rules for when
|
||||
# an instance is appended to the list of another instance
|
||||
|
||||
sess = session._state_session(state)
|
||||
@@ -51,7 +51,7 @@ def track_cascade_events(descriptor, prop):
|
||||
sess.expunge(item)
|
||||
|
||||
def set_(state, newvalue, oldvalue, initiator):
|
||||
# process "save_update" cascade rules for when an instance
|
||||
# process "save_update" cascade rules for when an instance
|
||||
# is attached to another instance
|
||||
if oldvalue is newvalue:
|
||||
return newvalue
|
||||
@@ -86,12 +86,12 @@ class UOWTransaction(object):
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
|
||||
# dictionary used by external actors to
|
||||
# dictionary used by external actors to
|
||||
# store arbitrary state information.
|
||||
self.attributes = {}
|
||||
|
||||
# dictionary of mappers to sets of
|
||||
# DependencyProcessors, which are also
|
||||
# dictionary of mappers to sets of
|
||||
# DependencyProcessors, which are also
|
||||
# set to be part of the sorted flush actions,
|
||||
# which have that mapper as a parent.
|
||||
self.deps = util.defaultdict(set)
|
||||
@@ -106,7 +106,7 @@ class UOWTransaction(object):
|
||||
# and determine if a flush action is needed
|
||||
self.presort_actions = {}
|
||||
|
||||
# dictionary of PostSortRec objects, each
|
||||
# dictionary of PostSortRec objects, each
|
||||
# one issues work during the flush within
|
||||
# a certain ordering.
|
||||
self.postsort_actions = {}
|
||||
@@ -124,7 +124,7 @@ class UOWTransaction(object):
|
||||
|
||||
# tracks InstanceStates which will be receiving
|
||||
# a "post update" call. Keys are mappers,
|
||||
# values are a set of states and a set of the
|
||||
# values are a set of states and a set of the
|
||||
# columns which should be included in the update.
|
||||
self.post_update_states = util.defaultdict(lambda: (set(), set()))
|
||||
|
||||
@@ -133,7 +133,7 @@ class UOWTransaction(object):
|
||||
return bool(self.states)
|
||||
|
||||
def is_deleted(self, state):
|
||||
"""return true if the given state is marked as deleted
|
||||
"""return true if the given state is marked as deleted
|
||||
within this uowtransaction."""
|
||||
|
||||
return state in self.states and self.states[state][0]
|
||||
@@ -152,7 +152,7 @@ class UOWTransaction(object):
|
||||
|
||||
self.states[state] = (isdelete, True)
|
||||
|
||||
def get_attribute_history(self, state, key,
|
||||
def get_attribute_history(self, state, key,
|
||||
passive=attributes.PASSIVE_NO_INITIALIZE):
|
||||
"""facade to attributes.get_state_history(), including caching of results."""
|
||||
|
||||
@@ -164,12 +164,12 @@ class UOWTransaction(object):
|
||||
|
||||
if hashkey in self.attributes:
|
||||
history, state_history, cached_passive = self.attributes[hashkey]
|
||||
# if the cached lookup was "passive" and now
|
||||
# if the cached lookup was "passive" and now
|
||||
# we want non-passive, do a non-passive lookup and re-cache
|
||||
if cached_passive is not attributes.PASSIVE_OFF \
|
||||
and passive is attributes.PASSIVE_OFF:
|
||||
impl = state.manager[key].impl
|
||||
history = impl.get_history(state, state.dict,
|
||||
history = impl.get_history(state, state.dict,
|
||||
attributes.PASSIVE_OFF)
|
||||
if history and impl.uses_objects:
|
||||
state_history = history.as_state()
|
||||
@@ -197,13 +197,13 @@ class UOWTransaction(object):
|
||||
if key not in self.presort_actions:
|
||||
self.presort_actions[key] = Preprocess(processor, fromparent)
|
||||
|
||||
def register_object(self, state, isdelete=False,
|
||||
def register_object(self, state, isdelete=False,
|
||||
listonly=False, cancel_delete=False,
|
||||
operation=None, prop=None):
|
||||
if not self.session._contains_state(state):
|
||||
if not state.deleted and operation is not None:
|
||||
util.warn("Object of type %s not in session, %s operation "
|
||||
"along '%s' will not proceed" %
|
||||
"along '%s' will not proceed" %
|
||||
(mapperutil.state_class_str(state), operation, prop))
|
||||
return False
|
||||
|
||||
@@ -228,8 +228,8 @@ class UOWTransaction(object):
|
||||
|
||||
@util.memoized_property
|
||||
def _mapper_for_dep(self):
|
||||
"""return a dynamic mapping of (Mapper, DependencyProcessor) to
|
||||
True or False, indicating if the DependencyProcessor operates
|
||||
"""return a dynamic mapping of (Mapper, DependencyProcessor) to
|
||||
True or False, indicating if the DependencyProcessor operates
|
||||
on objects of that Mapper.
|
||||
|
||||
The result is stored in the dictionary persistently once
|
||||
@@ -241,7 +241,7 @@ class UOWTransaction(object):
|
||||
)
|
||||
|
||||
def filter_states_for_dep(self, dep, states):
|
||||
"""Filter the given list of InstanceStates to those relevant to the
|
||||
"""Filter the given list of InstanceStates to those relevant to the
|
||||
given DependencyProcessor.
|
||||
|
||||
"""
|
||||
@@ -273,7 +273,7 @@ class UOWTransaction(object):
|
||||
|
||||
# see if the graph of mapper dependencies has cycles.
|
||||
self.cycles = cycles = topological.find_cycles(
|
||||
self.dependencies,
|
||||
self.dependencies,
|
||||
self.postsort_actions.values())
|
||||
|
||||
if cycles:
|
||||
@@ -319,14 +319,14 @@ class UOWTransaction(object):
|
||||
# execute
|
||||
if self.cycles:
|
||||
for set_ in topological.sort_as_subsets(
|
||||
self.dependencies,
|
||||
self.dependencies,
|
||||
postsort_actions):
|
||||
while set_:
|
||||
n = set_.pop()
|
||||
n.execute_aggregate(self, set_)
|
||||
else:
|
||||
for rec in topological.sort(
|
||||
self.dependencies,
|
||||
self.dependencies,
|
||||
postsort_actions):
|
||||
rec.execute(self)
|
||||
|
||||
@@ -470,7 +470,7 @@ class SaveUpdateAll(PostSortRec):
|
||||
assert mapper is mapper.base_mapper
|
||||
|
||||
def execute(self, uow):
|
||||
persistence.save_obj(self.mapper,
|
||||
persistence.save_obj(self.mapper,
|
||||
uow.states_for_mapper_hierarchy(self.mapper, False, False),
|
||||
uow
|
||||
)
|
||||
@@ -478,8 +478,8 @@ class SaveUpdateAll(PostSortRec):
|
||||
def per_state_flush_actions(self, uow):
|
||||
states = list(uow.states_for_mapper_hierarchy(self.mapper, False, False))
|
||||
for rec in self.mapper._per_state_flush_actions(
|
||||
uow,
|
||||
states,
|
||||
uow,
|
||||
states,
|
||||
False):
|
||||
yield rec
|
||||
|
||||
@@ -501,8 +501,8 @@ class DeleteAll(PostSortRec):
|
||||
def per_state_flush_actions(self, uow):
|
||||
states = list(uow.states_for_mapper_hierarchy(self.mapper, True, False))
|
||||
for rec in self.mapper._per_state_flush_actions(
|
||||
uow,
|
||||
states,
|
||||
uow,
|
||||
states,
|
||||
True):
|
||||
yield rec
|
||||
|
||||
@@ -520,8 +520,8 @@ class ProcessState(PostSortRec):
|
||||
cls_ = self.__class__
|
||||
dependency_processor = self.dependency_processor
|
||||
delete = self.delete
|
||||
our_recs = [r for r in recs
|
||||
if r.__class__ is cls_ and
|
||||
our_recs = [r for r in recs
|
||||
if r.__class__ is cls_ and
|
||||
r.dependency_processor is dependency_processor and
|
||||
r.delete is delete]
|
||||
recs.difference_update(our_recs)
|
||||
@@ -547,13 +547,13 @@ class SaveUpdateState(PostSortRec):
|
||||
def execute_aggregate(self, uow, recs):
|
||||
cls_ = self.__class__
|
||||
mapper = self.mapper
|
||||
our_recs = [r for r in recs
|
||||
if r.__class__ is cls_ and
|
||||
our_recs = [r for r in recs
|
||||
if r.__class__ is cls_ and
|
||||
r.mapper is mapper]
|
||||
recs.difference_update(our_recs)
|
||||
persistence.save_obj(mapper,
|
||||
[self.state] +
|
||||
[r.state for r in our_recs],
|
||||
[self.state] +
|
||||
[r.state for r in our_recs],
|
||||
uow)
|
||||
|
||||
def __repr__(self):
|
||||
@@ -570,13 +570,13 @@ class DeleteState(PostSortRec):
|
||||
def execute_aggregate(self, uow, recs):
|
||||
cls_ = self.__class__
|
||||
mapper = self.mapper
|
||||
our_recs = [r for r in recs
|
||||
if r.__class__ is cls_ and
|
||||
our_recs = [r for r in recs
|
||||
if r.__class__ is cls_ and
|
||||
r.mapper is mapper]
|
||||
recs.difference_update(our_recs)
|
||||
states = [self.state] + [r.state for r in our_recs]
|
||||
persistence.delete_obj(mapper,
|
||||
[s for s in states if uow.states[s][0]],
|
||||
[s for s in states if uow.states[s][0]],
|
||||
uow)
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
+73
-60
@@ -1,5 +1,5 @@
|
||||
# orm/util.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -30,15 +30,15 @@ class CascadeOptions(frozenset):
|
||||
|
||||
def __new__(cls, arg):
|
||||
values = set([
|
||||
c for c
|
||||
c for c
|
||||
in re.split('\s*,\s*', arg or "")
|
||||
if c
|
||||
])
|
||||
|
||||
if values.difference(cls._allowed_cascades):
|
||||
raise sa_exc.ArgumentError(
|
||||
"Invalid cascade option(s): %s" %
|
||||
", ".join([repr(x) for x in
|
||||
"Invalid cascade option(s): %s" %
|
||||
", ".join([repr(x) for x in
|
||||
sorted(
|
||||
values.difference(cls._allowed_cascades)
|
||||
)])
|
||||
@@ -68,30 +68,42 @@ class CascadeOptions(frozenset):
|
||||
",".join([x for x in sorted(self)])
|
||||
)
|
||||
|
||||
def _validator_events(desc, key, validator):
|
||||
def _validator_events(desc, key, validator, include_removes):
|
||||
"""Runs a validation method on an attribute value to be set or appended."""
|
||||
|
||||
def append(state, value, initiator):
|
||||
return validator(state.obj(), key, value)
|
||||
if include_removes:
|
||||
def append(state, value, initiator):
|
||||
return validator(state.obj(), key, value, False)
|
||||
|
||||
def set_(state, value, oldvalue, initiator):
|
||||
return validator(state.obj(), key, value)
|
||||
def set_(state, value, oldvalue, initiator):
|
||||
return validator(state.obj(), key, value, False)
|
||||
|
||||
def remove(state, value, initiator):
|
||||
validator(state.obj(), key, value, True)
|
||||
else:
|
||||
def append(state, value, initiator):
|
||||
return validator(state.obj(), key, value)
|
||||
|
||||
def set_(state, value, oldvalue, initiator):
|
||||
return validator(state.obj(), key, value)
|
||||
|
||||
event.listen(desc, 'append', append, raw=True, retval=True)
|
||||
event.listen(desc, 'set', set_, raw=True, retval=True)
|
||||
if include_removes:
|
||||
event.listen(desc, "remove", remove, raw=True, retval=True)
|
||||
|
||||
def polymorphic_union(table_map, typecolname, aliasname='p_union', cast_nulls=True):
|
||||
"""Create a ``UNION`` statement used by a polymorphic mapper.
|
||||
|
||||
See :ref:`concrete_inheritance` for an example of how
|
||||
this is used.
|
||||
|
||||
:param table_map: mapping of polymorphic identities to
|
||||
|
||||
:param table_map: mapping of polymorphic identities to
|
||||
:class:`.Table` objects.
|
||||
:param typecolname: string name of a "discriminator" column, which will be
|
||||
:param typecolname: string name of a "discriminator" column, which will be
|
||||
derived from the query, producing the polymorphic identity for each row. If
|
||||
``None``, no polymorphic discriminator is generated.
|
||||
:param aliasname: name of the :func:`~sqlalchemy.sql.expression.alias()`
|
||||
:param aliasname: name of the :func:`~sqlalchemy.sql.expression.alias()`
|
||||
construct generated.
|
||||
:param cast_nulls: if True, non-existent columns, which are represented as labeled
|
||||
NULLs, will be passed into CAST. This is a legacy behavior that is problematic
|
||||
@@ -105,7 +117,7 @@ def polymorphic_union(table_map, typecolname, aliasname='p_union', cast_nulls=Tr
|
||||
for key in table_map.keys():
|
||||
table = table_map[key]
|
||||
|
||||
# mysql doesnt like selecting from a select;
|
||||
# mysql doesnt like selecting from a select;
|
||||
# make it an alias of the select
|
||||
if isinstance(table, sql.Select):
|
||||
table = table.alias()
|
||||
@@ -187,7 +199,7 @@ def identity_key(*args, **kwargs):
|
||||
% ", ".join(kwargs.keys()))
|
||||
mapper = class_mapper(class_)
|
||||
if "ident" in locals():
|
||||
return mapper.identity_key_from_primary_key(ident)
|
||||
return mapper.identity_key_from_primary_key(util.to_list(ident))
|
||||
return mapper.identity_key_from_row(row)
|
||||
instance = kwargs.pop("instance")
|
||||
if kwargs:
|
||||
@@ -203,14 +215,14 @@ class ORMAdapter(sql_util.ColumnAdapter):
|
||||
and the AliasedClass if any is referenced.
|
||||
|
||||
"""
|
||||
def __init__(self, entity, equivalents=None,
|
||||
def __init__(self, entity, equivalents=None,
|
||||
chain_to=None, adapt_required=False):
|
||||
self.mapper, selectable, is_aliased_class = _entity_info(entity)
|
||||
if is_aliased_class:
|
||||
self.aliased_class = entity
|
||||
else:
|
||||
self.aliased_class = None
|
||||
sql_util.ColumnAdapter.__init__(self, selectable,
|
||||
sql_util.ColumnAdapter.__init__(self, selectable,
|
||||
equivalents, chain_to,
|
||||
adapt_required=adapt_required)
|
||||
|
||||
@@ -236,51 +248,51 @@ class AliasedClass(object):
|
||||
session.query(User, user_alias).\\
|
||||
join((user_alias, User.id > user_alias.id)).\\
|
||||
filter(User.name==user_alias.name)
|
||||
|
||||
|
||||
The resulting object is an instance of :class:`.AliasedClass`, however
|
||||
it implements a ``__getattribute__()`` scheme which will proxy attribute
|
||||
access to that of the ORM class being aliased. All classmethods
|
||||
on the mapped entity should also be available here, including
|
||||
on the mapped entity should also be available here, including
|
||||
hybrids created with the :ref:`hybrids_toplevel` extension,
|
||||
which will receive the :class:`.AliasedClass` as the "class" argument
|
||||
when classmethods are called.
|
||||
|
||||
|
||||
:param cls: ORM mapped entity which will be "wrapped" around an alias.
|
||||
:param alias: a selectable, such as an :func:`.alias` or :func:`.select`
|
||||
construct, which will be rendered in place of the mapped table of the
|
||||
ORM entity. If left as ``None``, an ordinary :class:`.Alias` of the
|
||||
ORM entity. If left as ``None``, an ordinary :class:`.Alias` of the
|
||||
ORM entity's mapped table will be generated.
|
||||
:param name: A name which will be applied both to the :class:`.Alias`
|
||||
if one is generated, as well as the name present in the "named tuple"
|
||||
returned by the :class:`.Query` object when results are returned.
|
||||
:param adapt_on_names: if True, more liberal "matching" will be used when
|
||||
mapping the mapped columns of the ORM entity to those of the given selectable -
|
||||
a name-based match will be performed if the given selectable doesn't
|
||||
otherwise have a column that corresponds to one on the entity. The
|
||||
mapping the mapped columns of the ORM entity to those of the given selectable -
|
||||
a name-based match will be performed if the given selectable doesn't
|
||||
otherwise have a column that corresponds to one on the entity. The
|
||||
use case for this is when associating an entity with some derived
|
||||
selectable such as one that uses aggregate functions::
|
||||
|
||||
|
||||
class UnitPrice(Base):
|
||||
__tablename__ = 'unit_price'
|
||||
...
|
||||
unit_id = Column(Integer)
|
||||
price = Column(Numeric)
|
||||
|
||||
|
||||
aggregated_unit_price = Session.query(
|
||||
func.sum(UnitPrice.price).label('price')
|
||||
).group_by(UnitPrice.unit_id).subquery()
|
||||
|
||||
|
||||
aggregated_unit_price = aliased(UnitPrice, alias=aggregated_unit_price, adapt_on_names=True)
|
||||
|
||||
|
||||
Above, functions on ``aggregated_unit_price`` which
|
||||
refer to ``.price`` will return the
|
||||
``fund.sum(UnitPrice.price).label('price')`` column,
|
||||
as it is matched on the name "price". Ordinarily, the "price" function wouldn't
|
||||
have any "column correspondence" to the actual ``UnitPrice.price`` column
|
||||
as it is not a proxy of the original.
|
||||
|
||||
``adapt_on_names`` is new in 0.7.3.
|
||||
|
||||
|
||||
.. versionadded:: 0.7.3
|
||||
|
||||
"""
|
||||
def __init__(self, cls, alias=None, name=None, adapt_on_names=False):
|
||||
self.__mapper = _class_to_mapper(cls)
|
||||
@@ -299,8 +311,8 @@ class AliasedClass(object):
|
||||
|
||||
def __getstate__(self):
|
||||
return {
|
||||
'mapper':self.__mapper,
|
||||
'alias':self.__alias,
|
||||
'mapper':self.__mapper,
|
||||
'alias':self.__alias,
|
||||
'name':self._sa_label_name,
|
||||
'adapt_on_names':self.__adapt_on_names,
|
||||
}
|
||||
@@ -321,7 +333,7 @@ class AliasedClass(object):
|
||||
def __adapt_element(self, elem):
|
||||
return self.__adapter.traverse(elem).\
|
||||
_annotate({
|
||||
'parententity': self,
|
||||
'parententity': self,
|
||||
'parentmapper':self.__mapper}
|
||||
)
|
||||
|
||||
@@ -388,7 +400,7 @@ class _ORMJoin(expression.Join):
|
||||
|
||||
__visit_name__ = expression.Join.__visit_name__
|
||||
|
||||
def __init__(self, left, right, onclause=None,
|
||||
def __init__(self, left, right, onclause=None,
|
||||
isouter=False, join_to_left=True):
|
||||
adapt_from = None
|
||||
|
||||
@@ -447,7 +459,7 @@ class _ORMJoin(expression.Join):
|
||||
|
||||
def join(left, right, onclause=None, isouter=False, join_to_left=True):
|
||||
"""Produce an inner join between left and right clauses.
|
||||
|
||||
|
||||
:func:`.orm.join` is an extension to the core join interface
|
||||
provided by :func:`.sql.expression.join()`, where the
|
||||
left and right selectables may be not only core selectable
|
||||
@@ -460,30 +472,30 @@ def join(left, right, onclause=None, isouter=False, join_to_left=True):
|
||||
in whatever form it is passed, to the selectable
|
||||
passed as the left side. If False, the onclause
|
||||
is used as is.
|
||||
|
||||
|
||||
:func:`.orm.join` is not commonly needed in modern usage,
|
||||
as its functionality is encapsulated within that of the
|
||||
:meth:`.Query.join` method, which features a
|
||||
significant amount of automation beyond :func:`.orm.join`
|
||||
by itself. Explicit usage of :func:`.orm.join`
|
||||
with :class:`.Query` involves usage of the
|
||||
by itself. Explicit usage of :func:`.orm.join`
|
||||
with :class:`.Query` involves usage of the
|
||||
:meth:`.Query.select_from` method, as in::
|
||||
|
||||
|
||||
from sqlalchemy.orm import join
|
||||
session.query(User).\\
|
||||
select_from(join(User, Address, User.addresses)).\\
|
||||
filter(Address.email_address=='foo@bar.com')
|
||||
|
||||
In modern SQLAlchemy the above join can be written more
|
||||
|
||||
In modern SQLAlchemy the above join can be written more
|
||||
succinctly as::
|
||||
|
||||
|
||||
session.query(User).\\
|
||||
join(User.addresses).\\
|
||||
filter(Address.email_address=='foo@bar.com')
|
||||
|
||||
See :meth:`.Query.join` for information on modern usage
|
||||
of ORM level joins.
|
||||
|
||||
|
||||
"""
|
||||
return _ORMJoin(left, right, onclause, isouter, join_to_left)
|
||||
|
||||
@@ -504,23 +516,24 @@ def with_parent(instance, prop):
|
||||
|
||||
The SQL rendered is the same as that rendered when a lazy loader
|
||||
would fire off from the given parent on that attribute, meaning
|
||||
that the appropriate state is taken from the parent object in
|
||||
that the appropriate state is taken from the parent object in
|
||||
Python without the need to render joins to the parent table
|
||||
in the rendered statement.
|
||||
|
||||
As of 0.6.4, this method accepts parent instances in all
|
||||
persistence states, including transient, persistent, and detached.
|
||||
Only the requisite primary key/foreign key attributes need to
|
||||
be populated. Previous versions didn't work with transient
|
||||
instances.
|
||||
.. versionchanged:: 0.6.4
|
||||
This method accepts parent instances in all
|
||||
persistence states, including transient, persistent, and detached.
|
||||
Only the requisite primary key/foreign key attributes need to
|
||||
be populated. Previous versions didn't work with transient
|
||||
instances.
|
||||
|
||||
:param instance:
|
||||
An instance which has some :func:`.relationship`.
|
||||
|
||||
:param property:
|
||||
String property name, or class-bound attribute, which indicates
|
||||
what relationship from the instance should be used to reconcile the
|
||||
parent/child relationship.
|
||||
what relationship from the instance should be used to reconcile the
|
||||
parent/child relationship.
|
||||
|
||||
"""
|
||||
if isinstance(prop, basestring):
|
||||
@@ -529,8 +542,8 @@ def with_parent(instance, prop):
|
||||
elif isinstance(prop, attributes.QueryableAttribute):
|
||||
prop = prop.property
|
||||
|
||||
return prop.compare(operators.eq,
|
||||
instance,
|
||||
return prop.compare(operators.eq,
|
||||
instance,
|
||||
value_is_parent=True)
|
||||
|
||||
|
||||
@@ -584,7 +597,7 @@ def _entity_descriptor(entity, key):
|
||||
return getattr(entity, key)
|
||||
except AttributeError:
|
||||
raise sa_exc.InvalidRequestError(
|
||||
"Entity '%s' has no property '%s'" %
|
||||
"Entity '%s' has no property '%s'" %
|
||||
(description, key)
|
||||
)
|
||||
|
||||
@@ -626,7 +639,7 @@ def object_mapper(instance):
|
||||
raise exc.UnmappedInstanceError(instance)
|
||||
|
||||
def class_mapper(class_, compile=True):
|
||||
"""Given a class, return the primary :class:`.Mapper` associated
|
||||
"""Given a class, return the primary :class:`.Mapper` associated
|
||||
with the key.
|
||||
|
||||
Raises :class:`.UnmappedClassError` if no mapping is configured
|
||||
@@ -640,8 +653,8 @@ def class_mapper(class_, compile=True):
|
||||
mapper = class_manager.mapper
|
||||
|
||||
except exc.NO_STATE:
|
||||
if not isinstance(class_, type):
|
||||
raise sa_exc.ArgumentError("Class object expected, got '%r'." % class_)
|
||||
if not isinstance(class_, type):
|
||||
raise sa_exc.ArgumentError("Class object expected, got '%r'." % class_)
|
||||
raise exc.UnmappedClassError(class_)
|
||||
|
||||
if compile and mapperlib.module._new_mappers:
|
||||
@@ -672,7 +685,7 @@ def has_identity(object):
|
||||
return state.has_identity
|
||||
|
||||
def _is_mapped_class(cls):
|
||||
"""Return True if the given object is a mapped class,
|
||||
"""Return True if the given object is a mapped class,
|
||||
:class:`.Mapper`, or :class:`.AliasedClass`."""
|
||||
|
||||
if isinstance(cls, (AliasedClass, mapperlib.Mapper)):
|
||||
@@ -685,7 +698,7 @@ def _is_mapped_class(cls):
|
||||
return False
|
||||
|
||||
def _mapper_or_none(cls):
|
||||
"""Return the :class:`.Mapper` for the given class or None if the
|
||||
"""Return the :class:`.Mapper` for the given class or None if the
|
||||
class is not mapped."""
|
||||
|
||||
manager = attributes.manager_of_class(cls)
|
||||
|
||||
Reference in New Issue
Block a user