First, read main branch README.md.
This branch shows my exploration of SQLAlchemy-Continuum to get history of changes for selected models.
Steps:
- https://sqlalchemy-continuum.readthedocs.io/en/latest/intro.html#basics
- Run
alembic -c development.ini revision --autogenerate -m "init"to generate newmodels_versionhistory table. - Run
alembic -c development.ini upgrade headto add the new table to DB. - Run
pserve development.ini --reloadand go tohttp://localhost:6543/. - On every view load
mymodel.valueis incremented by1.
Now you can see two new tables, models_version and transaction, in your database.
The models_version table holds history of the mymodel objects.
The transactions table holds metadata of every change: when it happend, by whom and from which IP.
Also see test_history.py for further details on how history works.
To see the list of all required changes to add object history via SQLAlchemy-Continuum to a Pyramid project, see the diff at https://github.com/zupo/tutorial/compare/main...exploration/sqlalchemy-continuum.
Well, since SQLAlchemy-Continuum is based on SQALchemy, not much changes for Alembic migrations -- changes to the schema are automatically reflected into changes for history tables, which are picked up by Alembic's autogenerate feature.
For example, suppose we change value = Column(Integer) to value = Column(Text) for MyModel in mymodel.py. The resulting migration created by Alembic looks like so:
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('models', 'value',
existing_type=sa.INTEGER(),
type_=sa.Text(),
existing_nullable=True)
op.alter_column('models_version', 'value',
existing_type=sa.INTEGER(),
type_=sa.Text(),
existing_nullable=True,
autoincrement=False)
# ### end Alembic commands ###I.e. the history table is updated along with the original table. Meaning historical versions need to be kept up-to-date with current schema, which makes working with older versions easier in other parts of the codebase (UI, for example) as we don't have to deal with objects that have outdated schemas.
- https://pypi.org/project/pyramid-sqlalchemy-continuum/ (repo no longer available)
- https://pypi.org/project/pyramid-sqlalchemy-continuum3/ (using
get_current_requestwhich is ill-advised)
I had to downgrade SQLAlchemy from 2.0.3 to 1.4.46, otherwise I get this error when running initialize_tutorial_db development.ini
Traceback (most recent call last):
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 4241, in _flush
self.dispatch.after_flush(self, flush_context)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy/event/attr.py", line 485, in __call__
fn(*args, **kw)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy_continuum/manager.py", line 354, in after_flush
uow.process_after_flush(session)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy_continuum/unit_of_work.py", line 95, in process_after_flush
self.make_versions(session)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy_continuum/unit_of_work.py", line 317, in make_versions
self.create_version_objects(session)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy_continuum/unit_of_work.py", line 202, in create_version_objects
self.process_operation(operation)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy_continuum/unit_of_work.py", line 175, in process_operation
self.update_version_validity(
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy_continuum/unit_of_work.py", line 251, in update_version_validity
subquery = self.version_validity_subquery(
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy_continuum/unit_of_work.py", line 220, in version_validity_subquery
subquery = fetcher._transaction_id_subquery(
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy_continuum/fetcher.py", line 72, in _transaction_id_subquery
sa.select(
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy/sql/_selectable_constructors.py", line 492, in select
raise _no_kw()
sqlalchemy.exc.ArgumentError: Additional keyword arguments are not accepted by this function/method. The presence of **kw is for pep-484 typing purposes
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/nix/store/f74shhzajyyr3bah2acqd00xh1ysfwpg-python3.10-poetry2nix-env-scripts/bin/.initialize_tutorial_db-wrapped", line 13, in <module>
sys.exit(main())
File "/Users/zupo/work/pyramid_tutorial/tutorial/scripts/initialize_db.py", line 34, in main
with env['request'].tm:
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/transaction/_manager.py", line 143, in __exit__
self.commit()
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/transaction/_manager.py", line 134, in commit
return self.get().commit()
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/transaction/_transaction.py", line 282, in commit
reraise(t, v, tb)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/transaction/_compat.py", line 49, in reraise
raise value
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/transaction/_transaction.py", line 273, in commit
self._commitResources()
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/transaction/_transaction.py", line 456, in _commitResources
reraise(t, v, tb)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/transaction/_compat.py", line 49, in reraise
raise value
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/transaction/_transaction.py", line 428, in _commitResources
rm.tpc_begin(self)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/zope/sqlalchemy/datamanager.py", line 129, in tpc_begin
self.session.flush()
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 4140, in flush
self._flush(objects)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 4276, in _flush
with util.safe_reraise():
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy/util/langhelpers.py", line 151, in __exit__
raise value.with_traceback(traceback)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 4277, in _flush
transaction.rollback(_capture_exception=True)
File "<string>", line 2, in rollback
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy/orm/state_changes.py", line 137, in _go
ret_value = fn(self, *arg, **kw)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 1302, in rollback
raise rollback_err[1].with_traceback(rollback_err[2])
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 1269, in rollback
self.session.dispatch.after_rollback(self.session)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy/event/attr.py", line 378, in __call__
fn(*args, **kw)
File "/nix/store/k6v3bzj64hmccg3nvhvqn7hrgwgvpyzd-python3-3.10.9-env/lib/python3.10/site-packages/sqlalchemy_continuum/manager.py", line 365, in clear
if session.transaction.nested:
AttributeError: 'Session' object has no attribute 'transaction'. Did you mean: '_transaction'?

