I think I did it right. The logs just didn't scroll up. This might be causing the issue (account is a orm.relationship).
api_1 | File "/app/models/post/__init__.py", line 160, in manage_review_status api_1 | if not target.account.settings.compliance_enable_post: Full logs: api_1 | Traceback (most recent call last): api_1 | File "/usr/local/lib/python3.5/site-packages/werkzeug/wsgi.py" , line 660, in __call__ api_1 | return app(environ, start_response) api_1 | File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 2000, in __call__ api_1 | return self.wsgi_app(environ, start_response) api_1 | File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1988, in wsgi_app api_1 | response = self.full_dispatch_request() api_1 | File "/usr/local/lib/python3.5/site-packages/flask_cors/extension.py", line 161, in wrapped_function api_1 | return cors_after_request(app.make_response(f(*args, ** kwargs))) api_1 | File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1567, in handle_exception api_1 | reraise(exc_type, exc_value, tb) api_1 | File "/usr/local/lib/python3.5/site-packages/flask/_compat.py" , line 33, in reraise api_1 | raise value api_1 | File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1988, in wsgi_app api_1 | response = self.full_dispatch_request() api_1 | File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1639, in full_dispatch_request api_1 | rv = self.dispatch_request() api_1 | File "/usr/local/lib/python3.5/site-packages/flask_cors/extension.py", line 161, in wrapped_function api_1 | return cors_after_request(app.make_response(f(*args, ** kwargs))) api_1 | File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1544, in handle_user_exception api_1 | reraise(exc_type, exc_value, tb) api_1 | File "/usr/local/lib/python3.5/site-packages/flask/_compat.py" , line 33, in reraise api_1 | raise value api_1 | File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1639, in full_dispatch_request api_1 | rv = self.dispatch_request() api_1 | File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1625, in dispatch_request api_1 | return self.view_functions[rule.endpoint](**req.view_args) api_1 | File "/app/common/errors.py", line 67, in decorator api_1 | return fn(*args, **kwargs) api_1 | File "/app/common/auth.py", line 295, in decorator api_1 | return fn(*args, **kwargs) api_1 | File "/app/common/auth.py", line 303, in decorator api_1 | return fn(*args, **kwargs) api_1 | File "/usr/local/lib/python3.5/site-packages/flask_principal.py", line 199, in _decorated api_1 | rv = f(*args, **kw) api_1 | File "/usr/local/lib/python3.5/site-packages/flask/views.py", line 84, in view api_1 | return self.dispatch_request(*args, **kwargs) api_1 | File "/app/common/resource.py", line 104, in dispatch_request api_1 | return action(*args, **kwargs) api_1 | File "/app/api/account/resources/pages/__init__.py", line 66, in patch api_1 | model = self.save(model) api_1 | File "/app/api/account/resources/pages/__init__.py", line 84, in save api_1 | model.revise() api_1 | File "/app/common/revision.py", line 77, in revise api_1 | execute_preprocessors(self) api_1 | File "/app/common/revision.py", line 84, in execute_preprocessors api_1 | processor(target) api_1 | File "/app/models/post/__init__.py", line 160, in manage_review_status api_1 | if not target.account.settings.compliance_enable_post: api_1 | File "/usr/local/lib/python3.5/site-packages/sqlalchemy/orm/attributes.py", line 237, in __get__ api_1 | return self.impl.get(instance_state(instance), dict_) api_1 | File "/usr/local/lib/python3.5/site-packages/sqlalchemy/orm/attributes.py", line 584, in get api_1 | value = self.callable_(state, passive) api_1 | File "/usr/local/lib/python3.5/site-packages/sqlalchemy/orm/strategies.py", line 557, in _load_for_state api_1 | return self._emit_lazyload(session, state, ident_key, passive) api_1 | File "<string>", line 1, in <lambda> api_1 | api_1 | File "/usr/local/lib/python3.5/site-packages/sqlalchemy/orm/strategies.py", line 635, in _emit_lazyload api_1 | result = q.all() api_1 | File "/usr/local/lib/python3.5/site-packages/sqlalchemy/orm/query.py", line 2703, in all api_1 | return list(self) api_1 | File "/usr/local/lib/python3.5/site-packages/sqlalchemy/orm/query.py", line 2854, in __iter__ api_1 | self.session._autoflush() api_1 | File "/usr/local/lib/python3.5/site-packages/sqlalchemy/orm/session.py", line 1365, in _autoflush api_1 | self.flush() api_1 | File "/usr/local/lib/python3.5/site-packages/sqlalchemy/orm/session.py", line 2139, in flush api_1 | self._flush(objects) api_1 | File "/app/common/sqlalchemy.py", line 17, in _flush api_1 | super()._flush(objects) api_1 | File "/app/common/sqlalchemy.py", line 17, in _flush api_1 | super()._flush(objects) api_1 | File "/usr/local/lib/python3.5/bdb.py", line 48, in trace_dispatch api_1 | return self.dispatch_line(frame) api_1 | File "/usr/local/lib/python3.5/bdb.py", line 67, in dispatch_line api_1 | if self.quitting: raise BdbQuit api_1 | bdb.BdbQuit On Monday, May 7, 2018 at 7:27:03 PM UTC-7, Mike Bayer wrote: > > can you perhaps place a "pdb.set_trace()" inside of session._flush()? > using the debugger you can see the source of every flush() call. > Generally, it occurs each time a query is about to emit SQL. > > On Mon, May 7, 2018 at 9:37 PM, Colton Allen <cmana...@gmail.com > <javascript:>> wrote: > > 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 sqlalchemy+...@googlegroups.com <javascript:>. > > To post to this group, send email to sqlal...@googlegroups.com > <javascript:>. > > Visit this group at https://groups.google.com/group/sqlalchemy. > > For more options, visit https://groups.google.com/d/optout. > -- 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 sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.