""" models.py 요구사항의 테이블을 SQLAlchemy ORM 모델로 정의합니다. 테이블: - users - categories - prompts - likes 핵심 포인트: - likes에는 (prompt_id, user_identifier) 유니크 제약을 걸어 "중복 좋아요"를 DB 레벨에서도 방지합니다. - 프롬프트 검색은 title/content LIKE로 구현합니다(초경량 MVP 목적). - 나중에 로그인(계정/세션)을 붙이기 쉽도록 users 테이블을 별도로 유지합니다. """ from __future__ import annotations from datetime import datetime from sqlalchemy import DateTime, ForeignKey, Index, Integer, String, Text, UniqueConstraint, func from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship class Base(DeclarativeBase): pass class User(Base): __tablename__ = "users" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) nickname: Mapped[str] = mapped_column(String(40), unique=True, index=True, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False) class Category(Base): __tablename__ = "categories" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) name: Mapped[str] = mapped_column(String(40), unique=True, nullable=False) slug: Mapped[str] = mapped_column(String(60), unique=True, index=True, nullable=False) prompts: Mapped[list["Prompt"]] = relationship(back_populates="category") class Prompt(Base): __tablename__ = "prompts" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) title: Mapped[str] = mapped_column(String(120), index=True, nullable=False) content: Mapped[str] = mapped_column(Text, nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) category_id: Mapped[int] = mapped_column(ForeignKey("categories.id"), index=True, nullable=False) # MVP에서는 로그인 없이 nickname 문자열로 작성자를 기록합니다. author_nickname: Mapped[str] = mapped_column(String(40), index=True, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), index=True, nullable=False) copy_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False) category: Mapped["Category"] = relationship(back_populates="prompts") likes: Mapped[list["Like"]] = relationship(back_populates="prompt", cascade="all, delete-orphan") __table_args__ = ( # 정렬/필터가 잦은 필드 위주로 인덱스(SQLite에서도 도움). Index("ix_prompts_category_created", "category_id", "created_at"), ) class Like(Base): __tablename__ = "likes" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) prompt_id: Mapped[int] = mapped_column(ForeignKey("prompts.id"), index=True, nullable=False) # 쿠키 UUID 또는 IP 기반 식별자를 해시한 값(개인정보 최소화) user_identifier: Mapped[str] = mapped_column(String(64), index=True, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False) prompt: Mapped["Prompt"] = relationship(back_populates="likes") __table_args__ = ( UniqueConstraint("prompt_id", "user_identifier", name="uq_likes_prompt_user"), )