Skip to content

Instantly share code, notes, and snippets.

@mazz
Created June 7, 2017 20:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mazz/7d63e521316859f4ae852e5cea5d84eb to your computer and use it in GitHub Desktop.
Save mazz/7d63e521316859f4ae852e5cea5d84eb to your computer and use it in GitHub Desktop.
Migration fails when changing to single table inheritance
class MediaBase(Base):
#: The table in the database
__tablename__ = "mediabase"
#: Database primary key for the row (running counter)
id = Column(Integer, autoincrement=True, primary_key=True)
# table inheritance
media_type = Column(String(32), nullable=False)
#: Publicly exposed non-guessable
uuid = Column(UUID(as_uuid=True), default=uuid4)
localizedname = Column(Unicode(128), default=None)
#: url
url = Column(Unicode(384), default=None)
# full iso language-locale identifier i.e. zh-Hans-US
language_id = Column(String(16), default=None)
# name of the person presenting the material
presenter_name = Column(Unicode(64), default=None)
source_material = Column(Unicode(128), default=None)
# table inheritance
__mapper_args__ = {'polymorphic_on': media_type}
def __repr__(self):
"""Shell and debugger presentation."""
return '{} ({}) {} <{}>'.format(self.localizedname, self.language_id, str(self.uuid), self.url)
def __str__(self):
"""Python default and admin UI string presentation."""
return '{} ({}) presenter: {} source: {} <{}>'.format(self.localizedname, self.language_id, self.presenter_name, self.source_material, self.url)
class MediaChapter(MediaBase):
#: The table in the database
__tablename__ = "mediachapter"
__mapper_args__ = {'polymorphic_identity': 'chapter'}
id = Column(Integer, ForeignKey('mediabase.id'), primary_key=True)
#: Which chapter this media is part of
chapter_id = Column(Integer, ForeignKey('chapter.id'))
chapter = relationship("Chapter", back_populates="mediachapter")
@mazz
Copy link
Author

mazz commented Jun 7, 2017

Upon calling ws-alembic -c kjvrvg/conf/development.ini -x packages=all upgrade head

ws-alembic -c kjvrvg/conf/development.ini -x packages=all upgrade head
[16:12:21] [websauna.system.core.redis create_redis] Creating a new Redis connection pool. Process 94046, thread MainThread, max_connections 16
/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/pyramid_tm/__init__.py:56: UserWarning: pyramid_tm removed support for the "tm.attempts" setting in version 2.0. To re-enable retry support enable pyramid_retry in your application.
  warnings.warn('pyramid_tm removed support for the "tm.attempts" '
[16:12:21] [websauna.system.devop.alembic parse_allowed_packages] Considering migrations for models in Python packages ['all']
[16:12:21] [websauna.system.devop.alembic run_alembic] Starting online migration engine on database connection Engine(postgresql://localhost/kjvrvg_dev) version history table alembic_history_kjvrvg
[16:12:21] [alembic.runtime.migration __init__] Context impl PostgresqlImpl.
[16:12:21] [alembic.runtime.migration __init__] Will assume transactional DDL.
[16:12:21] [alembic.runtime.migration run_migrations] Running upgrade e74ba4203098 -> a00980918d75, MediaBase-MediaChapter subclassing
Traceback (most recent call last):
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1182, in _execute_context
    context)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/sqlalchemy/engine/default.py", line 470, in do_execute
    cursor.execute(statement, parameters)
psycopg2.IntegrityError: insert or update on table "mediachapter" violates foreign key constraint "fk_mediachapter_id_mediabase"
DETAIL:  Key (id)=(570) is not present in table "mediabase".


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/bin/ws-alembic", line 11, in <module>
    load_entry_point('websauna', 'console_scripts', 'ws-alembic')()
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/src/websauna/websauna/system/devop/scripts/alembic.py", line 15, in main
    load_entry_point('alembic', 'console_scripts', 'alembic')()
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/config.py", line 479, in main
    CommandLine(prog=prog).main(argv=argv)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/config.py", line 473, in main
    self.run_cmd(cfg, options)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/config.py", line 456, in run_cmd
    **dict((k, getattr(options, k, None)) for k in kwarg)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/command.py", line 254, in upgrade
    script.run_env()
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/script/base.py", line 416, in run_env
    util.load_python_file(self.dir, 'env.py')
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/util/pyfiles.py", line 93, in load_python_file
    module = load_module_py(module_id, path)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/util/compat.py", line 64, in load_module_py
    module_id, path).load_module(module_id)
  File "<frozen importlib._bootstrap_external>", line 399, in _check_name_wrapper
  File "<frozen importlib._bootstrap_external>", line 823, in load_module
  File "<frozen importlib._bootstrap_external>", line 682, in load_module
  File "<frozen importlib._bootstrap>", line 251, in _load_module_shim
  File "<frozen importlib._bootstrap>", line 675, in _load
  File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 205, in _call_with_frames_removed
  File "alembic/env.py", line 3, in <module>
    alembic.run_alembic(package="kjvrvg")
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/src/websauna/websauna/system/devop/alembic.py", line 242, in run_alembic
    run_migrations_online(engine, target_metadata, version_table, include_object)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/src/websauna/websauna/system/devop/alembic.py", line 99, in run_migrations_online
    context.run_migrations()
  File "<string>", line 8, in run_migrations
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/runtime/environment.py", line 817, in run_migrations
    self.get_context().run_migrations(**kw)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/runtime/migration.py", line 323, in run_migrations
    step.migration_fn(**kw)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvg/alembic/versions/a00980918d75_mediabase_mediachapter_subclassing.py", line 44, in upgrade
    op.create_foreign_key(op.f('fk_mediachapter_id_mediabase'), 'mediachapter', 'mediabase', ['id'], ['id'])
  File "<string>", line 8, in create_foreign_key
  File "<string>", line 3, in create_foreign_key
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/operations/ops.py", line 570, in create_foreign_key
    return operations.invoke(op)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/operations/base.py", line 318, in invoke
    return fn(self, operation)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/operations/toimpl.py", line 135, in create_constraint
    operation.to_constraint(operations.migration_context)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/ddl/impl.py", line 180, in add_constraint
    self._exec(schema.AddConstraint(const))
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/alembic/ddl/impl.py", line 118, in _exec
    return conn.execute(construct, *multiparams, **params)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 945, in execute
    return meth(self, multiparams, params)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/sqlalchemy/sql/ddl.py", line 68, in _execute_on_connection
    return connection._execute_ddl(self, multiparams, params)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1002, in _execute_ddl
    compiled
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1189, in _execute_context
    context)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1402, in _handle_dbapi_exception
    exc_info
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/sqlalchemy/util/compat.py", line 203, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb, cause=cause)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/sqlalchemy/util/compat.py", line 186, in reraise
    raise value.with_traceback(tb)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1182, in _execute_context
    context)
  File "/Users/michael/tmp/.sidha/files.noindex/websauna-kjvrvg/kjvrvgenv/lib/python3.6/site-packages/sqlalchemy/engine/default.py", line 470, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) insert or update on table "mediachapter" violates foreign key constraint "fk_mediachapter_id_mediabase"
DETAIL:  Key (id)=(570) is not present in table "mediabase".
 [SQL: 'ALTER TABLE mediachapter ADD CONSTRAINT fk_mediachapter_id_mediabase FOREIGN KEY(id) REFERENCES mediabase (id)']

@mazz
Copy link
Author

mazz commented Jun 7, 2017


"""MediaBase-MediaChapter subclassing

Revision ID: a00980918d75
Revises: e74ba4203098
Create Date: 2017-06-07 16:10:29.807437

"""

# revision identifiers, used by Alembic.
revision = 'a00980918d75'
down_revision = 'e74ba4203098'
branch_labels = None
depends_on = None

import datetime
import websauna.system.model.columns
from sqlalchemy.types import Text  # Needed from proper creation of JSON fields as Alembic inserts astext_type=Text() row

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('mediabase',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('media_type', sa.String(length=32), nullable=False),
    sa.Column('uuid', postgresql.UUID(as_uuid=True), nullable=True),
    sa.Column('localizedname', sa.Unicode(length=128), nullable=True),
    sa.Column('url', sa.Unicode(length=384), nullable=True),
    sa.Column('language_id', sa.String(length=16), nullable=True),
    sa.Column('presenter_name', sa.Unicode(length=64), nullable=True),
    sa.Column('source_material', sa.Unicode(length=128), nullable=True),
    sa.PrimaryKeyConstraint('id', name=op.f('pk_mediabase'))
    )
    op.alter_column('group', 'created_at',
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=websauna.system.model.columns.UTCDateTime(),
               existing_nullable=True)
    op.alter_column('group', 'updated_at',
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=websauna.system.model.columns.UTCDateTime(),
               existing_nullable=True)
    op.create_foreign_key(op.f('fk_mediachapter_id_mediabase'), 'mediachapter', 'mediabase', ['id'], ['id'])
    op.drop_column('mediachapter', 'source_material')
    op.drop_column('mediachapter', 'presenter_name')
    op.drop_column('mediachapter', 'localizedname')
    op.drop_column('mediachapter', 'url')
    op.drop_column('mediachapter', 'uuid')
    op.drop_column('mediachapter', 'language_id')
    op.alter_column('user_activation', 'created_at',
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=websauna.system.model.columns.UTCDateTime(),
               existing_nullable=True)
    op.alter_column('user_activation', 'expires_at',
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=websauna.system.model.columns.UTCDateTime(),
               existing_nullable=False)
    op.alter_column('user_activation', 'updated_at',
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=websauna.system.model.columns.UTCDateTime(),
               existing_nullable=True)
    op.alter_column('users', 'activated_at',
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=websauna.system.model.columns.UTCDateTime(),
               existing_nullable=True)
    op.alter_column('users', 'created_at',
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=websauna.system.model.columns.UTCDateTime(),
               existing_nullable=True)
    op.alter_column('users', 'last_auth_sensitive_operation_at',
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=websauna.system.model.columns.UTCDateTime(),
               existing_nullable=True)
    op.alter_column('users', 'last_login_at',
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=websauna.system.model.columns.UTCDateTime(),
               existing_nullable=True)
    op.alter_column('users', 'updated_at',
               existing_type=postgresql.TIMESTAMP(timezone=True),
               type_=websauna.system.model.columns.UTCDateTime(),
               existing_nullable=True)
    # ### end Alembic commands ###


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.alter_column('users', 'updated_at',
               existing_type=websauna.system.model.columns.UTCDateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=True)
    op.alter_column('users', 'last_login_at',
               existing_type=websauna.system.model.columns.UTCDateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=True)
    op.alter_column('users', 'last_auth_sensitive_operation_at',
               existing_type=websauna.system.model.columns.UTCDateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=True)
    op.alter_column('users', 'created_at',
               existing_type=websauna.system.model.columns.UTCDateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=True)
    op.alter_column('users', 'activated_at',
               existing_type=websauna.system.model.columns.UTCDateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=True)
    op.alter_column('user_activation', 'updated_at',
               existing_type=websauna.system.model.columns.UTCDateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=True)
    op.alter_column('user_activation', 'expires_at',
               existing_type=websauna.system.model.columns.UTCDateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=False)
    op.alter_column('user_activation', 'created_at',
               existing_type=websauna.system.model.columns.UTCDateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=True)
    op.add_column('mediachapter', sa.Column('language_id', sa.VARCHAR(length=16), autoincrement=False, nullable=True))
    op.add_column('mediachapter', sa.Column('uuid', postgresql.UUID(), autoincrement=False, nullable=True))
    op.add_column('mediachapter', sa.Column('url', sa.VARCHAR(length=384), autoincrement=False, nullable=True))
    op.add_column('mediachapter', sa.Column('localizedname', sa.VARCHAR(length=128), autoincrement=False, nullable=True))
    op.add_column('mediachapter', sa.Column('presenter_name', sa.VARCHAR(length=64), autoincrement=False, nullable=True))
    op.add_column('mediachapter', sa.Column('source_material', sa.VARCHAR(length=128), autoincrement=False, nullable=True))
    op.drop_constraint(op.f('fk_mediachapter_id_mediabase'), 'mediachapter', type_='foreignkey')
    op.alter_column('group', 'updated_at',
               existing_type=websauna.system.model.columns.UTCDateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=True)
    op.alter_column('group', 'created_at',
               existing_type=websauna.system.model.columns.UTCDateTime(),
               type_=postgresql.TIMESTAMP(timezone=True),
               existing_nullable=True)
    op.drop_table('mediabase')
    # ### end Alembic commands ###

@mazz
Copy link
Author

mazz commented Jun 7, 2017

class Chapter(Base):

    #: The table in the database
    __tablename__ = "chapter"

    #: Database primary key for the row (running counter)
    id = Column(Integer, autoincrement=True, primary_key=True)
    absolute_id = Column(Integer)

    #: Publicly exposed non-guessable id
    uuid = Column(UUID(as_uuid=True), default=uuid4)
    basename = Column(Unicode(64), default=None)

    book_id = Column(Integer, ForeignKey('book.id'))
    book = relationship("Book", back_populates="chapters")

    #: Relationship mapping between mediachapter and chapter.
    #: Each mediachapter can have only one chapter.
    #: Deleting chapter deletes its mediachapter.
    mediachapter = relationship("MediaChapter",
                           back_populates="chapter",
                           lazy="dynamic",
                           cascade="all, delete-orphan",
                           single_parent=True)

    def __repr__(self):
        """Shell and debugger presentation."""
        return "{} {}".format(self.book.basename, self.id)

    def __str__(self):
        """Python default and admin UI string presentation."""
        if hasattr(self.book, 'basename'):
            return "{} {}".format(self.book.basename, self.absolute_id)
        else:
            return "{} {}".format('(not set)', self.absolute_id)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment