Add core data model (12 tables) and initial Alembic migration
All core entities from ARCHITECTURE §5: tenancy (User, Tree, TreeMembership), people (Person, Name, Relationship), facts (Event, Place, PlaceName), provenance (Source, Citation), and the append-only AuditEntry. Cross-cutting mixins give every row a UUID key, timestamps, soft delete, and (where tree-owned) a tree_id for uniform tenant isolation. Modeling choices: parentage as qualified edges (biological/adoptive/step/foster/donor/guardian) so non-traditional families are first-class; events keep both a verbatim date string and a normalized start/end range; closed sets are PG enums while GEDCOM-extensible vocabularies (event/name/source type) stay strings; CHECK constraints enforce single-subject events and single-target citations. Place is tree-scoped in Phase 0 (see ARCHITECTURE note). The migration is verified reversible (upgrade/downgrade drops tables and enum types) and matches the models (alembic check clean); applied on the deploy target. Dockerfile now ships migrations. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Justin Paul <justin@jpaul.me>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
"""Alembic environment — async, URL sourced from settings (DATABASE_URL)."""
|
||||
|
||||
import asyncio
|
||||
from logging.config import fileConfig
|
||||
|
||||
from alembic import context
|
||||
from sqlalchemy import pool
|
||||
from sqlalchemy.ext.asyncio import async_engine_from_config
|
||||
|
||||
from app.core.config import get_settings
|
||||
from app.models import Base # noqa: F401 — importing registers all models
|
||||
|
||||
config = context.config
|
||||
if config.config_file_name is not None:
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
# Inject the runtime database URL (asyncpg driver) from the environment.
|
||||
config.set_main_option("sqlalchemy.url", get_settings().database_url)
|
||||
|
||||
target_metadata = Base.metadata
|
||||
|
||||
|
||||
def run_migrations_offline() -> None:
|
||||
context.configure(
|
||||
url=config.get_main_option("sqlalchemy.url"),
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
compare_type=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
)
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def _do_run_migrations(connection) -> None:
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
compare_type=True,
|
||||
)
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
async def run_migrations_online() -> None:
|
||||
connectable = async_engine_from_config(
|
||||
config.get_section(config.config_ini_section, {}),
|
||||
prefix="sqlalchemy.",
|
||||
poolclass=pool.NullPool,
|
||||
)
|
||||
async with connectable.connect() as connection:
|
||||
await connection.run_sync(_do_run_migrations)
|
||||
await connectable.dispose()
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
asyncio.run(run_migrations_online())
|
||||
@@ -0,0 +1,26 @@
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
from collections.abc import Sequence
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = ${repr(up_revision)}
|
||||
down_revision: str | None = ${repr(down_revision)}
|
||||
branch_labels: str | Sequence[str] | None = ${repr(branch_labels)}
|
||||
depends_on: str | Sequence[str] | None = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
${downgrades if downgrades else "pass"}
|
||||
@@ -0,0 +1,304 @@
|
||||
"""core data model
|
||||
|
||||
Revision ID: ec43c338e155
|
||||
Revises:
|
||||
Create Date: 2026-06-06 10:27:41.671787
|
||||
|
||||
"""
|
||||
from collections.abc import Sequence
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'ec43c338e155'
|
||||
down_revision: str | None = None
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('users',
|
||||
sa.Column('email', sa.String(length=320), nullable=False),
|
||||
sa.Column('email_verified_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column('display_name', sa.String(length=255), nullable=True),
|
||||
sa.Column('hashed_password', sa.String(length=255), nullable=True),
|
||||
sa.Column('id', sa.Uuid(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_users'))
|
||||
)
|
||||
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
|
||||
op.create_table('trees',
|
||||
sa.Column('owner_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('visibility', sa.Enum('public', 'unlisted', 'private', name='tree_visibility'), server_default='private', nullable=False),
|
||||
sa.Column('id', sa.Uuid(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.ForeignKeyConstraint(['owner_id'], ['users.id'], name=op.f('fk_trees_owner_id_users'), ondelete='RESTRICT'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_trees'))
|
||||
)
|
||||
op.create_index(op.f('ix_trees_owner_id'), 'trees', ['owner_id'], unique=False)
|
||||
op.create_table('audit_entries',
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('tree_id', sa.Uuid(), nullable=True),
|
||||
sa.Column('actor_type', sa.Enum('user', 'assistant', name='audit_actor_type'), server_default='user', nullable=False),
|
||||
sa.Column('actor_user_id', sa.Uuid(), nullable=True),
|
||||
sa.Column('action', sa.String(length=64), nullable=False),
|
||||
sa.Column('entity_type', sa.String(length=64), nullable=False),
|
||||
sa.Column('entity_id', sa.Uuid(), nullable=True),
|
||||
sa.Column('before', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||||
sa.Column('after', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||||
sa.Column('id', sa.Uuid(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['actor_user_id'], ['users.id'], name=op.f('fk_audit_entries_actor_user_id_users'), ondelete='SET NULL'),
|
||||
sa.ForeignKeyConstraint(['tree_id'], ['trees.id'], name=op.f('fk_audit_entries_tree_id_trees'), ondelete='SET NULL'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_audit_entries'))
|
||||
)
|
||||
op.create_index(op.f('ix_audit_entries_actor_user_id'), 'audit_entries', ['actor_user_id'], unique=False)
|
||||
op.create_index(op.f('ix_audit_entries_created_at'), 'audit_entries', ['created_at'], unique=False)
|
||||
op.create_index(op.f('ix_audit_entries_tree_id'), 'audit_entries', ['tree_id'], unique=False)
|
||||
op.create_table('persons',
|
||||
sa.Column('gender', sa.String(length=32), nullable=True),
|
||||
sa.Column('is_living', sa.Boolean(), nullable=True),
|
||||
sa.Column('privacy', sa.Enum('inherit', 'private', 'public', name='person_privacy'), server_default='inherit', nullable=False),
|
||||
sa.Column('notes', sa.Text(), nullable=True),
|
||||
sa.Column('id', sa.Uuid(), nullable=False),
|
||||
sa.Column('tree_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.ForeignKeyConstraint(['tree_id'], ['trees.id'], name=op.f('fk_persons_tree_id_trees'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_persons'))
|
||||
)
|
||||
op.create_index(op.f('ix_persons_tree_id'), 'persons', ['tree_id'], unique=False)
|
||||
op.create_table('places',
|
||||
sa.Column('name', sa.String(length=512), nullable=False),
|
||||
sa.Column('parent_id', sa.Uuid(), nullable=True),
|
||||
sa.Column('place_type', sa.String(length=64), nullable=True),
|
||||
sa.Column('latitude', sa.Float(), nullable=True),
|
||||
sa.Column('longitude', sa.Float(), nullable=True),
|
||||
sa.Column('id', sa.Uuid(), nullable=False),
|
||||
sa.Column('tree_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.ForeignKeyConstraint(['parent_id'], ['places.id'], name=op.f('fk_places_parent_id_places'), ondelete='SET NULL'),
|
||||
sa.ForeignKeyConstraint(['tree_id'], ['trees.id'], name=op.f('fk_places_tree_id_trees'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_places'))
|
||||
)
|
||||
op.create_index(op.f('ix_places_parent_id'), 'places', ['parent_id'], unique=False)
|
||||
op.create_index(op.f('ix_places_tree_id'), 'places', ['tree_id'], unique=False)
|
||||
op.create_table('sources',
|
||||
sa.Column('title', sa.String(length=512), nullable=False),
|
||||
sa.Column('author', sa.String(length=255), nullable=True),
|
||||
sa.Column('source_type', sa.String(length=64), nullable=True),
|
||||
sa.Column('repository', sa.String(length=255), nullable=True),
|
||||
sa.Column('url', sa.String(length=1024), nullable=True),
|
||||
sa.Column('citation_text', sa.Text(), nullable=True),
|
||||
sa.Column('publication_info', sa.Text(), nullable=True),
|
||||
sa.Column('quality_note', sa.String(length=255), nullable=True),
|
||||
sa.Column('id', sa.Uuid(), nullable=False),
|
||||
sa.Column('tree_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.ForeignKeyConstraint(['tree_id'], ['trees.id'], name=op.f('fk_sources_tree_id_trees'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_sources'))
|
||||
)
|
||||
op.create_index(op.f('ix_sources_tree_id'), 'sources', ['tree_id'], unique=False)
|
||||
op.create_table('tree_memberships',
|
||||
sa.Column('tree_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('user_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('role', sa.Enum('owner', 'editor', 'viewer', name='membership_role'), nullable=False),
|
||||
sa.Column('id', sa.Uuid(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['tree_id'], ['trees.id'], name=op.f('fk_tree_memberships_tree_id_trees'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name=op.f('fk_tree_memberships_user_id_users'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_tree_memberships')),
|
||||
sa.UniqueConstraint('tree_id', 'user_id', name='uq_tree_memberships_tree_user')
|
||||
)
|
||||
op.create_index(op.f('ix_tree_memberships_tree_id'), 'tree_memberships', ['tree_id'], unique=False)
|
||||
op.create_index(op.f('ix_tree_memberships_user_id'), 'tree_memberships', ['user_id'], unique=False)
|
||||
op.create_table('names',
|
||||
sa.Column('person_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('name_type', sa.String(length=32), server_default='birth', nullable=False),
|
||||
sa.Column('given', sa.String(length=255), nullable=True),
|
||||
sa.Column('surname', sa.String(length=255), nullable=True),
|
||||
sa.Column('prefix', sa.String(length=64), nullable=True),
|
||||
sa.Column('suffix', sa.String(length=64), nullable=True),
|
||||
sa.Column('nickname', sa.String(length=128), nullable=True),
|
||||
sa.Column('display_name', sa.String(length=512), nullable=True),
|
||||
sa.Column('is_primary', sa.Boolean(), server_default=sa.text('false'), nullable=False),
|
||||
sa.Column('sort_order', sa.Integer(), server_default='0', nullable=False),
|
||||
sa.Column('id', sa.Uuid(), nullable=False),
|
||||
sa.Column('tree_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.ForeignKeyConstraint(['person_id'], ['persons.id'], name=op.f('fk_names_person_id_persons'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tree_id'], ['trees.id'], name=op.f('fk_names_tree_id_trees'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_names'))
|
||||
)
|
||||
op.create_index(op.f('ix_names_person_id'), 'names', ['person_id'], unique=False)
|
||||
op.create_index(op.f('ix_names_tree_id'), 'names', ['tree_id'], unique=False)
|
||||
op.create_table('place_names',
|
||||
sa.Column('place_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('name', sa.String(length=512), nullable=False),
|
||||
sa.Column('valid_from', sa.Date(), nullable=True),
|
||||
sa.Column('valid_to', sa.Date(), nullable=True),
|
||||
sa.Column('id', sa.Uuid(), nullable=False),
|
||||
sa.Column('tree_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.ForeignKeyConstraint(['place_id'], ['places.id'], name=op.f('fk_place_names_place_id_places'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tree_id'], ['trees.id'], name=op.f('fk_place_names_tree_id_trees'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_place_names'))
|
||||
)
|
||||
op.create_index(op.f('ix_place_names_place_id'), 'place_names', ['place_id'], unique=False)
|
||||
op.create_index(op.f('ix_place_names_tree_id'), 'place_names', ['tree_id'], unique=False)
|
||||
op.create_table('relationships',
|
||||
sa.Column('type', sa.Enum('parent_child', 'partnership', 'sibling', name='relationship_type'), nullable=False),
|
||||
sa.Column('person_from_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('person_to_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('qualifier', sa.Enum('biological', 'adoptive', 'step', 'foster', 'donor', 'guardian', name='parent_child_qualifier'), nullable=True),
|
||||
sa.Column('notes', sa.Text(), nullable=True),
|
||||
sa.Column('id', sa.Uuid(), nullable=False),
|
||||
sa.Column('tree_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.CheckConstraint('person_from_id <> person_to_id', name=op.f('ck_relationships_different_persons')),
|
||||
sa.ForeignKeyConstraint(['person_from_id'], ['persons.id'], name=op.f('fk_relationships_person_from_id_persons'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['person_to_id'], ['persons.id'], name=op.f('fk_relationships_person_to_id_persons'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tree_id'], ['trees.id'], name=op.f('fk_relationships_tree_id_trees'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_relationships'))
|
||||
)
|
||||
op.create_index(op.f('ix_relationships_person_from_id'), 'relationships', ['person_from_id'], unique=False)
|
||||
op.create_index(op.f('ix_relationships_person_to_id'), 'relationships', ['person_to_id'], unique=False)
|
||||
op.create_index(op.f('ix_relationships_tree_id'), 'relationships', ['tree_id'], unique=False)
|
||||
op.create_table('events',
|
||||
sa.Column('event_type', sa.String(length=64), nullable=False),
|
||||
sa.Column('person_id', sa.Uuid(), nullable=True),
|
||||
sa.Column('relationship_id', sa.Uuid(), nullable=True),
|
||||
sa.Column('place_id', sa.Uuid(), nullable=True),
|
||||
sa.Column('date_value', sa.String(length=255), nullable=True),
|
||||
sa.Column('date_start', sa.Date(), nullable=True),
|
||||
sa.Column('date_end', sa.Date(), nullable=True),
|
||||
sa.Column('date_precision', sa.String(length=32), nullable=True),
|
||||
sa.Column('calendar', sa.String(length=32), server_default='gregorian', nullable=False),
|
||||
sa.Column('detail', sa.String(length=512), nullable=True),
|
||||
sa.Column('notes', sa.Text(), nullable=True),
|
||||
sa.Column('id', sa.Uuid(), nullable=False),
|
||||
sa.Column('tree_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.CheckConstraint('(person_id IS NOT NULL) <> (relationship_id IS NOT NULL)', name=op.f('ck_events_subject_person_xor_relationship')),
|
||||
sa.ForeignKeyConstraint(['person_id'], ['persons.id'], name=op.f('fk_events_person_id_persons'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['place_id'], ['places.id'], name=op.f('fk_events_place_id_places'), ondelete='SET NULL'),
|
||||
sa.ForeignKeyConstraint(['relationship_id'], ['relationships.id'], name=op.f('fk_events_relationship_id_relationships'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tree_id'], ['trees.id'], name=op.f('fk_events_tree_id_trees'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_events'))
|
||||
)
|
||||
op.create_index(op.f('ix_events_event_type'), 'events', ['event_type'], unique=False)
|
||||
op.create_index(op.f('ix_events_person_id'), 'events', ['person_id'], unique=False)
|
||||
op.create_index(op.f('ix_events_place_id'), 'events', ['place_id'], unique=False)
|
||||
op.create_index(op.f('ix_events_relationship_id'), 'events', ['relationship_id'], unique=False)
|
||||
op.create_index(op.f('ix_events_tree_id'), 'events', ['tree_id'], unique=False)
|
||||
op.create_table('citations',
|
||||
sa.Column('source_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('person_id', sa.Uuid(), nullable=True),
|
||||
sa.Column('event_id', sa.Uuid(), nullable=True),
|
||||
sa.Column('name_id', sa.Uuid(), nullable=True),
|
||||
sa.Column('relationship_id', sa.Uuid(), nullable=True),
|
||||
sa.Column('page', sa.String(length=255), nullable=True),
|
||||
sa.Column('detail', sa.Text(), nullable=True),
|
||||
sa.Column('confidence', sa.Enum('high', 'medium', 'low', name='citation_confidence'), nullable=True),
|
||||
sa.Column('id', sa.Uuid(), nullable=False),
|
||||
sa.Column('tree_id', sa.Uuid(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.CheckConstraint('(person_id IS NOT NULL)::int + (event_id IS NOT NULL)::int + (name_id IS NOT NULL)::int + (relationship_id IS NOT NULL)::int = 1', name=op.f('ck_citations_exactly_one_target')),
|
||||
sa.ForeignKeyConstraint(['event_id'], ['events.id'], name=op.f('fk_citations_event_id_events'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['name_id'], ['names.id'], name=op.f('fk_citations_name_id_names'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['person_id'], ['persons.id'], name=op.f('fk_citations_person_id_persons'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['relationship_id'], ['relationships.id'], name=op.f('fk_citations_relationship_id_relationships'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['source_id'], ['sources.id'], name=op.f('fk_citations_source_id_sources'), ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['tree_id'], ['trees.id'], name=op.f('fk_citations_tree_id_trees'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_citations'))
|
||||
)
|
||||
op.create_index(op.f('ix_citations_event_id'), 'citations', ['event_id'], unique=False)
|
||||
op.create_index(op.f('ix_citations_name_id'), 'citations', ['name_id'], unique=False)
|
||||
op.create_index(op.f('ix_citations_person_id'), 'citations', ['person_id'], unique=False)
|
||||
op.create_index(op.f('ix_citations_relationship_id'), 'citations', ['relationship_id'], unique=False)
|
||||
op.create_index(op.f('ix_citations_source_id'), 'citations', ['source_id'], unique=False)
|
||||
op.create_index(op.f('ix_citations_tree_id'), 'citations', ['tree_id'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_citations_tree_id'), table_name='citations')
|
||||
op.drop_index(op.f('ix_citations_source_id'), table_name='citations')
|
||||
op.drop_index(op.f('ix_citations_relationship_id'), table_name='citations')
|
||||
op.drop_index(op.f('ix_citations_person_id'), table_name='citations')
|
||||
op.drop_index(op.f('ix_citations_name_id'), table_name='citations')
|
||||
op.drop_index(op.f('ix_citations_event_id'), table_name='citations')
|
||||
op.drop_table('citations')
|
||||
op.drop_index(op.f('ix_events_tree_id'), table_name='events')
|
||||
op.drop_index(op.f('ix_events_relationship_id'), table_name='events')
|
||||
op.drop_index(op.f('ix_events_place_id'), table_name='events')
|
||||
op.drop_index(op.f('ix_events_person_id'), table_name='events')
|
||||
op.drop_index(op.f('ix_events_event_type'), table_name='events')
|
||||
op.drop_table('events')
|
||||
op.drop_index(op.f('ix_relationships_tree_id'), table_name='relationships')
|
||||
op.drop_index(op.f('ix_relationships_person_to_id'), table_name='relationships')
|
||||
op.drop_index(op.f('ix_relationships_person_from_id'), table_name='relationships')
|
||||
op.drop_table('relationships')
|
||||
op.drop_index(op.f('ix_place_names_tree_id'), table_name='place_names')
|
||||
op.drop_index(op.f('ix_place_names_place_id'), table_name='place_names')
|
||||
op.drop_table('place_names')
|
||||
op.drop_index(op.f('ix_names_tree_id'), table_name='names')
|
||||
op.drop_index(op.f('ix_names_person_id'), table_name='names')
|
||||
op.drop_table('names')
|
||||
op.drop_index(op.f('ix_tree_memberships_user_id'), table_name='tree_memberships')
|
||||
op.drop_index(op.f('ix_tree_memberships_tree_id'), table_name='tree_memberships')
|
||||
op.drop_table('tree_memberships')
|
||||
op.drop_index(op.f('ix_sources_tree_id'), table_name='sources')
|
||||
op.drop_table('sources')
|
||||
op.drop_index(op.f('ix_places_tree_id'), table_name='places')
|
||||
op.drop_index(op.f('ix_places_parent_id'), table_name='places')
|
||||
op.drop_table('places')
|
||||
op.drop_index(op.f('ix_persons_tree_id'), table_name='persons')
|
||||
op.drop_table('persons')
|
||||
op.drop_index(op.f('ix_audit_entries_tree_id'), table_name='audit_entries')
|
||||
op.drop_index(op.f('ix_audit_entries_created_at'), table_name='audit_entries')
|
||||
op.drop_index(op.f('ix_audit_entries_actor_user_id'), table_name='audit_entries')
|
||||
op.drop_table('audit_entries')
|
||||
op.drop_index(op.f('ix_trees_owner_id'), table_name='trees')
|
||||
op.drop_table('trees')
|
||||
op.drop_index(op.f('ix_users_email'), table_name='users')
|
||||
op.drop_table('users')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
# Enum types are created implicitly by create_table() but not dropped by
|
||||
# drop_table(); drop them explicitly so downgrade is fully reversible.
|
||||
for enum_name in (
|
||||
"tree_visibility",
|
||||
"membership_role",
|
||||
"person_privacy",
|
||||
"relationship_type",
|
||||
"parent_child_qualifier",
|
||||
"citation_confidence",
|
||||
"audit_actor_type",
|
||||
):
|
||||
op.execute(f"DROP TYPE IF EXISTS {enum_name}")
|
||||
Reference in New Issue
Block a user