feat(trash): SoftDeleteMixin applied to the 7 soft-deletable models

This commit is contained in:
2026-05-28 19:54:01 -04:00
parent 84b75f7a73
commit 7f0d99d383
6 changed files with 22 additions and 11 deletions
+10 -1
View File
@@ -1,9 +1,18 @@
from datetime import datetime, timezone from datetime import datetime, timezone
from sqlalchemy import DateTime from sqlalchemy import DateTime, Text
from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.orm import Mapped, mapped_column
class SoftDeleteMixin:
"""Recoverable-delete columns. NULL deleted_at = live row. deleted_batch_id
groups rows soft-deleted in one operation so a cascade restores as a unit."""
deleted_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True, default=None
)
deleted_batch_id: Mapped[str | None] = mapped_column(Text, nullable=True, default=None)
class TimestampMixin: class TimestampMixin:
created_at: Mapped[datetime] = mapped_column( created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
+2 -1
View File
@@ -4,9 +4,10 @@ from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, Text
from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.orm import Mapped, mapped_column
from fabledassistant.models import Base from fabledassistant.models import Base
from fabledassistant.models.base import SoftDeleteMixin
class Event(Base): class Event(Base, SoftDeleteMixin):
__tablename__ = "events" __tablename__ = "events"
id: Mapped[int] = mapped_column(primary_key=True) id: Mapped[int] = mapped_column(primary_key=True)
+2 -2
View File
@@ -2,10 +2,10 @@ from sqlalchemy import ForeignKey, Integer, Text
from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.orm import Mapped, mapped_column
from fabledassistant.models import Base from fabledassistant.models import Base
from fabledassistant.models.base import TimestampMixin from fabledassistant.models.base import TimestampMixin, SoftDeleteMixin
class Milestone(Base, TimestampMixin): class Milestone(Base, TimestampMixin, SoftDeleteMixin):
__tablename__ = "milestones" __tablename__ = "milestones"
id: Mapped[int] = mapped_column(primary_key=True) id: Mapped[int] = mapped_column(primary_key=True)
+2 -2
View File
@@ -6,7 +6,7 @@ from sqlalchemy.dialects.postgresql import ARRAY, JSONB
from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.orm import Mapped, mapped_column
from fabledassistant.models import Base from fabledassistant.models import Base
from fabledassistant.models.base import TimestampMixin from fabledassistant.models.base import TimestampMixin, SoftDeleteMixin
class TaskStatus(str, enum.Enum): class TaskStatus(str, enum.Enum):
@@ -23,7 +23,7 @@ class TaskPriority(str, enum.Enum):
high = "high" high = "high"
class Note(Base, TimestampMixin): class Note(Base, TimestampMixin, SoftDeleteMixin):
__tablename__ = "notes" __tablename__ = "notes"
id: Mapped[int] = mapped_column(primary_key=True) id: Mapped[int] = mapped_column(primary_key=True)
+2 -2
View File
@@ -3,7 +3,7 @@ from datetime import datetime
from sqlalchemy import DateTime, ForeignKey, Integer, Text from sqlalchemy import DateTime, ForeignKey, Integer, Text
from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.orm import Mapped, mapped_column
from fabledassistant.models import Base from fabledassistant.models import Base
from fabledassistant.models.base import TimestampMixin from fabledassistant.models.base import TimestampMixin, SoftDeleteMixin
class ProjectStatus(str, enum.Enum): class ProjectStatus(str, enum.Enum):
@@ -12,7 +12,7 @@ class ProjectStatus(str, enum.Enum):
archived = "archived" archived = "archived"
class Project(Base, TimestampMixin): class Project(Base, TimestampMixin, SoftDeleteMixin):
__tablename__ = "projects" __tablename__ = "projects"
id: Mapped[int] = mapped_column(primary_key=True) id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True) user_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True)
+4 -3
View File
@@ -4,9 +4,10 @@ from sqlalchemy import BigInteger, Column, DateTime, ForeignKey, Integer, Table,
from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.orm import Mapped, mapped_column
from fabledassistant.models import Base from fabledassistant.models import Base
from fabledassistant.models.base import SoftDeleteMixin
class Rulebook(Base): class Rulebook(Base, SoftDeleteMixin):
__tablename__ = "rulebooks" __tablename__ = "rulebooks"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True) id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
@@ -35,7 +36,7 @@ class Rulebook(Base):
} }
class RulebookTopic(Base): class RulebookTopic(Base, SoftDeleteMixin):
__tablename__ = "rulebook_topics" __tablename__ = "rulebook_topics"
__table_args__ = ( __table_args__ = (
UniqueConstraint("rulebook_id", "title", name="uq_topic_per_rulebook"), UniqueConstraint("rulebook_id", "title", name="uq_topic_per_rulebook"),
@@ -69,7 +70,7 @@ class RulebookTopic(Base):
} }
class Rule(Base): class Rule(Base, SoftDeleteMixin):
__tablename__ = "rules" __tablename__ = "rules"
__table_args__ = ( __table_args__ = (
UniqueConstraint("topic_id", "title", name="uq_rule_per_topic"), UniqueConstraint("topic_id", "title", name="uq_rule_per_topic"),