summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2011-10-26 12:41:18 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2011-10-26 12:41:18 -0400
commitd9adb2a4fd3e865d3c8d4f6f2e0a12d5c4036c97 (patch)
treec00b308c373d2dae57c09e65d206e4be619b1ab6 /lib/sqlalchemy
parent8301651428be5396b76f7d20c8f61b5558d5a971 (diff)
downloadsqlalchemy-d9adb2a4fd3e865d3c8d4f6f2e0a12d5c4036c97.tar.gz
- [bug] the value of a composite attribute is now
expired after an insert or update operation, instead of regenerated in place. This ensures that a column value which is expired within a flush will be loaded first, before the composite is regenerated using that value. [ticket:2309] - [bug] The fix in [ticket:2309] also emits the "refresh" event when the composite value is loaded on access, even if all column values were already present, as is appropriate. This fixes the "mutable" extension which relies upon the "load" event to ensure the _parents dictionary is up to date, fixes [ticket:2308]. Thanks to Scott Torborg for the test case here.
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/descriptor_props.py28
1 files changed, 20 insertions, 8 deletions
diff --git a/lib/sqlalchemy/orm/descriptor_props.py b/lib/sqlalchemy/orm/descriptor_props.py
index 594705a8a..c4d59defa 100644
--- a/lib/sqlalchemy/orm/descriptor_props.py
+++ b/lib/sqlalchemy/orm/descriptor_props.py
@@ -104,6 +104,7 @@ class CompositeProperty(DescriptorProperty):
def fget(instance):
dict_ = attributes.instance_dict(instance)
+ state = attributes.instance_state(instance)
if self.key not in dict_:
# key not present. Iterate through related
@@ -111,11 +112,17 @@ class CompositeProperty(DescriptorProperty):
# ensures they all load.
values = [getattr(instance, key) for key in self._attribute_keys]
- # usually, the load() event will have loaded our key
- # at this point, unless we only loaded relationship()
- # attributes above. Populate here if that's the case.
- if self.key not in dict_ and not _none_set.issuperset(values):
+ # 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
+ # 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
+ not _none_set.issuperset(values)
+ ):
dict_[self.key] = self.composite_class(*values)
+ state.manager.dispatch.refresh(state, None, [self.key])
return dict_.get(self.key, None)
@@ -197,6 +204,7 @@ class CompositeProperty(DescriptorProperty):
if k not in dict_:
return
+ #assert self.key not in dict_
dict_[self.key] = self.composite_class(
*[state.dict[key] for key in
self._attribute_keys]
@@ -207,10 +215,14 @@ class CompositeProperty(DescriptorProperty):
state.dict.pop(self.key, None)
def insert_update_handler(mapper, connection, state):
- state.dict[self.key] = self.composite_class(
- *[state.dict.get(key, None) for key in
- self._attribute_keys]
- )
+ """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
+ recreates.
+
+ """
+
+ state.dict.pop(self.key, None)
event.listen(self.parent, 'after_insert',
insert_update_handler, raw=True)