well since create_version() is called within before_flush(), the net effect of any changes on local many-to-one relationships can only be taken into account by looking at these attributes. The test in test_versioning called test_relationship illustrates this.
so the diff is like this, the test still passes:
diff --git a/examples/versioned_history/history_meta.py
b/examples/versioned_history/history_meta.py
index 8cb5234..e8d3ed2 100644
--- a/examples/versioned_history/history_meta.py
+++ b/examples/versioned_history/history_meta.py
@@ -6,6 +6,7 @@ from sqlalchemy.orm.exc import UnmappedClassError,
UnmappedColumnError
from sqlalchemy import Table, Column, ForeignKeyConstraint, Integer
from sqlalchemy import event
from sqlalchemy.orm.properties import RelationshipProperty
+from sqlalchemy.orm.attributes import PASSIVE_NO_FETCH_RELATED
def col_references_table(col, table):
for fk in col.foreign_keys:
@@ -160,7 +161,7 @@ def create_version(obj, session, deleted = False):
# check those too
for prop in obj_mapper.iterate_properties:
if isinstance(prop, RelationshipProperty) and \
- attributes.get_history(obj, prop.key).has_changes():
+ attributes.get_history(obj, prop.key,
passive=PASSIVE_NO_FETCH_RELATED).has_changes():
for p in prop.local_columns:
if p.foreign_keys:
obj_changed = True
On Jan 24, 2014, at 5:48 PM, Simon King <[email protected]> wrote:
> I see the same behaviour on 0.8.2 and 0.9.1.
>
> Before making any changes I’d just like to understand what the code is trying
> to do. As far as I can tell, the first loop works up the inheritance chain,
> looking at each column to see if it changed, which is all fine.
>
> But then, if it hasn’t found any columns that have changed, it goes into the
> second loop, over obj_mapper.iterate_properties. I guess it is looking for
> relationships which don’t correspond to columns on the object itself (eg. the
> other side of a 1-to-many relationship). I can’t think of a situation where
> this will produce something that can actually be written to the history table.
>
> I removed the second loop altogether and in the very simple test it behaved
> exactly as I would have expected, but perhaps it’ll fail in more complicate
> scenarios?
>
> Thanks again,
>
> Simon
>
> On 24 Jan 2014, at 18:00, Michael Bayer <[email protected]> wrote:
>
>> without looking too deeply, you can modify that get_history() call to not
>> emit any SQL, assuming you’re on 0.9 (maybe on 0.8 also but the API has been
>> fixed up).
>>
>> Take a look at passing the “passive” flag to get_history(), or alternatively
>> using inspect().attrs.data.history:
>>
>> http://docs.sqlalchemy.org/en/rel_0_9/changelog/migration_09.html#attributes-get-history-will-query-from-the-db-by-default-if-value-not-present
>>
>>
>> I’m not sure if you’re on 0.8 or 0.9 but I would think 0.9 if that’s
>> emitting SQL? again, haven’t looked closely.
>>
>> if this fixes your issue we might want to update the recipe also, not sure.
>>
>>
>>
>> On Jan 24, 2014, at 11:22 AM, Simon King <[email protected]> wrote:
>>
>>> Hi again,
>>>
>>> While testing the complicated relationship from the other thread, I
>>> noticed that I was generating a lot of queries that I couldn't
>>> immediately explain. After a bit of digging, it turned out that it was
>>> due to my use of code based on the "versioned objects" example at
>>> http://docs.sqlalchemy.org/en/rel_0_9/_modules/examples/versioned_history/history_meta.html.
>>>
>>> In the test script below we have 3 classes. Users belong to Companies,
>>> and Companies reference a single Terms row. In the example, only the
>>> Company class is versioned.
>>>
>>> The test script inserts a single user, company and terms row, then in
>>> new transaction loads the company and adds a new user. If you look at
>>> the SQL output, you should see that it loads the company.terms
>>> relationship, which doesn't seem like it should be necessary.
>>>
>>> I think it's due to the following code from history_meta.py:
>>>
>>> if not obj_changed:
>>> # not changed, but we have relationships. OK
>>> # check those too
>>> for prop in obj_mapper.iterate_properties:
>>> if isinstance(prop, RelationshipProperty) and \
>>> attributes.get_history(obj, prop.key).has_changes():
>>> for p in prop.local_columns:
>>> if p.foreign_keys:
>>> obj_changed = True
>>> break
>>> if obj_changed is True:
>>> break
>>>
>>> I can't figure out the circumstances when this should be necessary -
>>> can someone explain it to me?
>>>
>>> Thanks a lot,
>>>
>>> Simon
>>>
>>> --
>>> You received this message because you are subscribed to the Google Groups
>>> "sqlalchemy" group.
>>> To unsubscribe from this group and stop receiving emails from it, send an
>>> email to [email protected].
>>> To post to this group, send email to [email protected].
>>> Visit this group at http://groups.google.com/group/sqlalchemy.
>>> For more options, visit https://groups.google.com/groups/opt_out.
>>
>
> --
> You received this message because you are subscribed to the Google Groups
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To post to this group, send email to [email protected].
> Visit this group at http://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/groups/opt_out.
signature.asc
Description: Message signed with OpenPGP using GPGMail
