diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-10-26 12:41:18 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-10-26 12:41:18 -0400 |
| commit | d9adb2a4fd3e865d3c8d4f6f2e0a12d5c4036c97 (patch) | |
| tree | c00b308c373d2dae57c09e65d206e4be619b1ab6 /lib/sqlalchemy | |
| parent | 8301651428be5396b76f7d20c8f61b5558d5a971 (diff) | |
| download | sqlalchemy-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.py | 28 |
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) |
