What exactly causes the session to flush? I'm trying to track down a nasty
bug in my versioning system.
Sorry for the long code dump. I retooled
examples/versioned_history/history_meta.py so it should look familiar. The
function that's breaking is "column_has_changed". I've added some logs as
well.
# WHEN IT WORKS!
CRITICAL:root:BEFORE MAPPING NEW VALUES
CRITICAL:root:BEFORE SAVE
CRITICAL:root:BEFORE REVISE
CRITICAL:root:CHECK COLUMN CHANGES
CRITICAL:root:AFTER REVISE
CRITICAL:root:flush!
CRITICAL:root:AFTER SAVE
CRITICAL:root:flush!
# WHEN IT DOESN'T WORK!
CRITICAL:root:BEFORE MAPPING NEW VALUES
CRITICAL:root:BEFORE SAVE
CRITICAL:root:BEFORE REVISE
CRITICAL:root:flush!
CRITICAL:root:CHECK COLUMN CHANGES
CRITICAL:root:AFTER REVISE
CRITICAL:root:AFTER SAVE
CRITICAL:root:flush!
controller.py
for k, v in dict.items():
setattr(model, k, v)
model.revise()
db.session.add(model)
db.session.commit()
model.py
class RevisionMixin:
"""Version control manager."""
def revise(self):
db.session.add(self)
write_revision(self)
version.py
def write_revision(target):
target_mapper = orm.object_mapper(target)
revision_class = target.__versioned__['model']
revision_mapper = revision_class.__mapper__
object_changed = False
state = {}
for column in iter_mapper_columns(target_mapper, revision_mapper):
state[column.key] = getattr(target, column.key)
column_changed = column_has_changed(target, column.key)
object_changed = object_changed or column_changed
for relationship, changed in iter_relationships(target, target_mapper):
if hasattr(revision_class, relationship.key):
state[relationship.key] = getattr(target, relationship.key)
object_changed = object_changed or changed
if not isinstance(target.id, str) or object_changed:
_write_revision(target, state)
def _write_revision(target, state):
version = target.version or 0
version = version + 1
state['version'] = version
state['updated_at'] = db.now()
state['primary'] = target
revision = target.__versioned__['model'](**state)
db.session.add(revision)
target.version = version
target.updated_at = state['updated_at']
def iter_mapper_columns(primary, revision):
mappers = zip(primary.iterate_to_root(), revision.iterate_to_root())
for om, hm in mappers:
if hm.single:
continue
for column in iter_shared_columns(om, hm):
yield column
def iter_shared_columns(mapper, comparison_mapper):
for comparison_mapper_column in comparison_mapper.local_table.c:
if 'version_meta' in comparison_mapper_column.info:
continue
try:
mapper_column = mapper.local_table.c[comparison_mapper_column.
key]
yield mapper.get_property_by_column(mapper_column)
except UnmappedColumnError:
continue
def iter_relationships(target, mapper):
for prop in mapper.iterate_properties:
if isinstance(prop, RelationshipProperty):
passive = attributes.PASSIVE_NO_INITIALIZE
changed = attributes.get_history(
target, prop.key, passive=passive).has_changes()
yield prop, changed
def column_has_changed(target, column_name):
# Sometimes the instance state history can't be properly
# calculated? No flushing during versioning. Unsure why its not
# working.
added, _, deleted = attributes.get_history(target, column_name)
return bool(added or deleted)
def relationship_has_changed(prop):
for p in prop.local_columns:
if p.foreign_keys:
return True
return False
--
SQLAlchemy -
The Python SQL Toolkit and Object Relational Mapper
http://www.sqlalchemy.org/
To post example code, please provide an MCVE: Minimal, Complete, and Verifiable
Example. See http://stackoverflow.com/help/mcve for a full description.
---
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 https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.