"""PostAttachment — a non-art file preserved from a post. Art images become ImageRecords; everything else a post contained (archives, .exe, .pdf, ...) is captured here so nothing is lost. post_id is nullable (set only when an adjacent sidecar yields a Post); artist_id mirrors the canonical attribution model (FC-2d-vii-c). Both FKs are SET NULL so deleting a Post/Artist never deletes the preserved binary row. """ from datetime import datetime from sqlalchemy import ( BigInteger, DateTime, ForeignKey, Integer, String, Text, func, ) from sqlalchemy.orm import Mapped, mapped_column from .base import Base class PostAttachment(Base): __tablename__ = "post_attachment" id: Mapped[int] = mapped_column(Integer, primary_key=True) post_id: Mapped[int | None] = mapped_column( ForeignKey("post.id", ondelete="SET NULL"), nullable=True, index=True ) artist_id: Mapped[int | None] = mapped_column( ForeignKey("artist.id", ondelete="SET NULL"), nullable=True, index=True ) sha256: Mapped[str] = mapped_column( String(64), nullable=False, unique=True, index=True ) path: Mapped[str] = mapped_column(Text, nullable=False) original_filename: Mapped[str] = mapped_column(Text, nullable=False) ext: Mapped[str] = mapped_column(String(32), nullable=False) mime: Mapped[str | None] = mapped_column(String(128), nullable=True) size_bytes: Mapped[int] = mapped_column(BigInteger, nullable=False) captured_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now() )