Adds deleted_at and deleted_by columns to trees table for proper soft delete tracking. Supports future 30-day restore window functionality. The delete endpoint now sets both is_active=False (backward compat) and deleted_at/deleted_by. Migration backfills existing is_active=False rows. Fixed ambiguous FK relationship between User/Tree models by adding explicit foreign_keys to both sides of the author relationship. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
39 lines
1.3 KiB
Python
39 lines
1.3 KiB
Python
"""add deleted_at and deleted_by columns to trees
|
|
|
|
Revision ID: 015
|
|
Revises: 014
|
|
Create Date: 2026-02-05
|
|
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
from sqlalchemy.dialects.postgresql import UUID
|
|
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = '015'
|
|
down_revision: Union[str, None] = '014'
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column('trees', sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True))
|
|
op.add_column('trees', sa.Column('deleted_by', UUID(as_uuid=True), nullable=True))
|
|
op.create_index('ix_trees_deleted_at', 'trees', ['deleted_at'])
|
|
op.create_foreign_key('fk_trees_deleted_by_users', 'trees', 'users', ['deleted_by'], ['id'])
|
|
|
|
# Backfill: convert existing is_active=False to deleted_at=now()
|
|
op.execute("UPDATE trees SET deleted_at = NOW() WHERE is_active = FALSE")
|
|
|
|
|
|
def downgrade() -> None:
|
|
# Convert back
|
|
op.execute("UPDATE trees SET is_active = FALSE WHERE deleted_at IS NOT NULL")
|
|
op.drop_constraint('fk_trees_deleted_by_users', 'trees', type_='foreignkey')
|
|
op.drop_index('ix_trees_deleted_at', table_name='trees')
|
|
op.drop_column('trees', 'deleted_by')
|
|
op.drop_column('trees', 'deleted_at')
|