Skip to content

zupo/tutorial

 
 

Repository files navigation

SQLAlchemy-Continuum exploration

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 new models_version history table.
  • Run alembic -c development.ini upgrade head to add the new table to DB.
  • Run pserve development.ini --reload and go to http://localhost:6543/.
  • On every view load mymodel.value is incremented by 1.

Now you can see two new tables, models_version and transaction, in your database.

Screenshot 2023-02-11 at 12 56 48

The models_version table holds history of the mymodel objects.

Screenshot 2023-02-11 at 12 56 58

The transactions table holds metadata of every change: when it happend, by whom and from which IP.

Screenshot 2023-02-11 at 12 57 10

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.

What happens when we do schema migrations?

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.

References

Caveats

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'?

About

Pyramid's "SQLAlchemy + URL dispatch" tutorial scaffold based on Poetry

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors