switched to tagging and added views to support it, in progress for other tagging functions.

This commit is contained in:
Bryan Van Deusen
2025-08-14 21:36:27 -04:00
parent e9793ba38c
commit 3ff34ec9c2
12 changed files with 943 additions and 349 deletions
@@ -0,0 +1,138 @@
"""Add ON DELETE CASCADE to image_tags; SET NULL for ArchiveRecord.tag_id
Revision ID: 486bf4238616
Revises: fe4dfdd6616c
Create Date: 2025-08-14 22:41:40.746201
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '486bf4238616'
down_revision = 'fe4dfdd6616c'
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
from alembic import op
import sqlalchemy as sa
# revision identifiers
revision = "486bf4238616"
down_revision = "fe4dfdd6616c"
branch_labels = None
depends_on = None
def upgrade():
bind = op.get_bind()
dialect = bind.dialect.name
if dialect == "sqlite":
# --- image_tags: recreate with CASCADE FKs ---
op.create_table(
"image_tags_new",
sa.Column("image_id", sa.Integer(), nullable=False),
sa.Column("tag_id", sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint("image_id", "tag_id"),
sa.ForeignKeyConstraint(["image_id"], ["image_record.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["tag_id"], ["tag.id"], ondelete="CASCADE"),
sa.UniqueConstraint("image_id", "tag_id", name="uq_image_tag"),
)
op.execute("INSERT INTO image_tags_new (image_id, tag_id) SELECT image_id, tag_id FROM image_tags")
op.drop_table("image_tags")
op.rename_table("image_tags_new", "image_tags")
# --- archive_record.tag_id: leave FK as-is on SQLite (dev) ---
# We do not alter ondelete here due to SQLite ALTER limitations.
# Your reset route deletes in safe order, so this is fine for testing.
else:
# --- Postgres path: drop/recreate FKs normally ---
# image_tags -> ON DELETE CASCADE
op.drop_constraint("image_tags_image_id_fkey", "image_tags", type_="foreignkey")
op.drop_constraint("image_tags_tag_id_fkey", "image_tags", type_="foreignkey")
op.create_foreign_key(
"image_tags_image_id_fkey",
"image_tags",
"image_record",
["image_id"],
["id"],
ondelete="CASCADE",
)
op.create_foreign_key(
"image_tags_tag_id_fkey",
"image_tags",
"tag",
["tag_id"],
["id"],
ondelete="CASCADE",
)
# archive_record.tag_id -> NULLable + ON DELETE SET NULL
op.alter_column("archive_record", "tag_id", existing_type=sa.Integer(), nullable=True)
op.drop_constraint("archive_record_tag_id_fkey", "archive_record", type_="foreignkey")
op.create_foreign_key(
"archive_record_tag_id_fkey",
"archive_record",
"tag",
["tag_id"],
["id"],
ondelete="SET NULL",
)
def downgrade():
bind = op.get_bind()
dialect = bind.dialect.name
if dialect == "sqlite":
# revert image_tags to non-cascade FKs (recreate again)
op.create_table(
"image_tags_old",
sa.Column("image_id", sa.Integer(), nullable=False),
sa.Column("tag_id", sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint("image_id", "tag_id"),
sa.ForeignKeyConstraint(["image_id"], ["image_record.id"]),
sa.ForeignKeyConstraint(["tag_id"], ["tag.id"]),
sa.UniqueConstraint("image_id", "tag_id", name="uq_image_tag"),
)
op.execute("INSERT INTO image_tags_old (image_id, tag_id) SELECT image_id, tag_id FROM image_tags")
op.drop_table("image_tags")
op.rename_table("image_tags_old", "image_tags")
# archive_record left unchanged in upgrade, nothing to revert here.
else:
# Postgres: revert to no-cascade FKs and non-nullable tag_id
op.drop_constraint("archive_record_tag_id_fkey", "archive_record", type_="foreignkey")
op.create_foreign_key(
"archive_record_tag_id_fkey",
"archive_record",
"tag",
["tag_id"],
["id"],
)
op.alter_column("archive_record", "tag_id", existing_type=sa.Integer(), nullable=False)
op.drop_constraint("image_tags_tag_id_fkey", "image_tags", type_="foreignkey")
op.drop_constraint("image_tags_image_id_fkey", "image_tags", type_="foreignkey")
op.create_foreign_key(
"image_tags_image_id_fkey",
"image_tags",
"image_record",
["image_id"],
["id"],
)
op.create_foreign_key(
"image_tags_tag_id_fkey",
"image_tags",
"tag",
["tag_id"],
["id"],
)
@@ -0,0 +1,39 @@
"""Drop ImageRecord.artist (use tags instead)
Revision ID: a46e641430b8
Revises: 486bf4238616
Create Date: 2025-08-15 00:52:13.293364
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'a46e641430b8'
down_revision = '486bf4238616'
branch_labels = None
depends_on = None
def upgrade():
bind = op.get_bind()
dialect = bind.dialect.name
if dialect == "sqlite":
# Recreate table without the 'artist' column (SQLite can't drop columns natively)
with op.batch_alter_table("image_record", recreate="always") as batch_op:
batch_op.drop_column("artist")
else:
# Postgres / others can drop directly
op.drop_column("image_record", "artist")
def downgrade():
bind = op.get_bind()
dialect = bind.dialect.name
if dialect == "sqlite":
with op.batch_alter_table("image_record", recreate="always") as batch_op:
batch_op.add_column(sa.Column("artist", sa.String(length=255), nullable=True))
else:
op.add_column("image_record", sa.Column("artist", sa.String(length=255), nullable=True))