252 lines
7.3 KiB
Python
252 lines
7.3 KiB
Python
"""
|
|
Home 모듈 SQLAlchemy 모델 정의
|
|
|
|
이 모듈은 영상 제작 파이프라인의 핵심 데이터 모델을 정의합니다.
|
|
- Project: 프로젝트(사용자 입력 이력) 관리
|
|
- Image: 업로드된 이미지 URL 관리
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import TYPE_CHECKING, List, Optional
|
|
|
|
from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Integer, String, Text, func
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from app.database.session import Base
|
|
|
|
if TYPE_CHECKING:
|
|
from app.lyric.models import Lyric
|
|
from app.song.models import Song
|
|
from app.user.models import User
|
|
from app.video.models import Video
|
|
|
|
|
|
class Project(Base):
|
|
"""
|
|
프로젝트 테이블 (사용자 입력 이력)
|
|
|
|
영상 제작 요청의 시작점으로, 고객 정보와 지역 정보를 저장합니다.
|
|
하위 테이블(Lyric, Song, Video)의 부모 테이블 역할을 합니다.
|
|
|
|
Attributes:
|
|
id: 고유 식별자 (자동 증가)
|
|
store_name: 고객명 (필수)
|
|
region: 지역명 (필수, 예: 서울, 부산, 대구 등)
|
|
task_id: 작업 고유 식별자 (UUID7 형식, 36자)
|
|
detail_region_info: 상세 지역 정보 (선택, JSON 또는 텍스트 형식)
|
|
created_at: 생성 일시 (자동 설정)
|
|
|
|
Relationships:
|
|
owner: 프로젝트 소유자 (User, 1:N 관계)
|
|
lyrics: 생성된 가사 목록
|
|
songs: 생성된 노래 목록
|
|
videos: 최종 영상 결과 목록
|
|
"""
|
|
|
|
__tablename__ = "project"
|
|
__table_args__ = (
|
|
Index("idx_project_task_id", "task_id"),
|
|
Index("idx_project_store_name", "store_name"),
|
|
Index("idx_project_region", "region"),
|
|
Index("idx_project_user_uuid", "user_uuid"),
|
|
Index("idx_project_is_deleted", "is_deleted"),
|
|
{
|
|
"mysql_engine": "InnoDB",
|
|
"mysql_charset": "utf8mb4",
|
|
"mysql_collate": "utf8mb4_unicode_ci",
|
|
},
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(
|
|
Integer,
|
|
primary_key=True,
|
|
nullable=False,
|
|
autoincrement=True,
|
|
comment="고유 식별자",
|
|
)
|
|
|
|
store_name: Mapped[str] = mapped_column(
|
|
String(255),
|
|
nullable=False,
|
|
comment="가게명",
|
|
)
|
|
|
|
region: Mapped[str] = mapped_column(
|
|
String(100),
|
|
nullable=False,
|
|
comment="지역명 (예: 군산)",
|
|
)
|
|
|
|
task_id: Mapped[str] = mapped_column(
|
|
String(36),
|
|
nullable=False,
|
|
unique=True,
|
|
comment="프로젝트 작업 고유 식별자 (UUID7)",
|
|
)
|
|
|
|
# ==========================================================================
|
|
# User 1:N 관계 (한 사용자가 여러 프로젝트를 소유)
|
|
# ==========================================================================
|
|
user_uuid: Mapped[Optional[str]] = mapped_column(
|
|
String(36),
|
|
ForeignKey("user.user_uuid", ondelete="SET NULL"),
|
|
nullable=True,
|
|
comment="프로젝트 소유자 (User.user_uuid 외래키)",
|
|
)
|
|
|
|
# 소유자 관계 설정 (User.projects와 양방향 연결)
|
|
owner: Mapped[Optional["User"]] = relationship(
|
|
"User",
|
|
back_populates="projects",
|
|
lazy="selectin",
|
|
)
|
|
|
|
detail_region_info: Mapped[Optional[str]] = mapped_column(
|
|
Text,
|
|
nullable=True,
|
|
comment="상세 지역 정보",
|
|
)
|
|
|
|
language: Mapped[str] = mapped_column(
|
|
String(50),
|
|
nullable=False,
|
|
default="Korean",
|
|
comment="출력 언어 (Korean, English, Chinese, Japanese, Thai, Vietnamese)",
|
|
)
|
|
|
|
is_deleted: Mapped[bool] = mapped_column(
|
|
Boolean,
|
|
nullable=False,
|
|
default=False,
|
|
comment="소프트 삭제 여부 (True: 삭제됨)",
|
|
)
|
|
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime,
|
|
nullable=False,
|
|
server_default=func.now(),
|
|
comment="생성 일시",
|
|
)
|
|
|
|
# Relationships
|
|
lyrics: Mapped[List["Lyric"]] = relationship(
|
|
"Lyric",
|
|
back_populates="project",
|
|
cascade="all, delete-orphan",
|
|
lazy="selectin",
|
|
)
|
|
|
|
songs: Mapped[List["Song"]] = relationship(
|
|
"Song",
|
|
back_populates="project",
|
|
cascade="all, delete-orphan",
|
|
lazy="selectin",
|
|
)
|
|
|
|
videos: Mapped[List["Video"]] = relationship(
|
|
"Video",
|
|
back_populates="project",
|
|
cascade="all, delete-orphan",
|
|
lazy="selectin",
|
|
)
|
|
|
|
def __repr__(self) -> str:
|
|
def truncate(value: str | None, max_len: int = 10) -> str:
|
|
if value is None:
|
|
return "None"
|
|
return (value[:max_len] + "...") if len(value) > max_len else value
|
|
|
|
return (
|
|
f"<Project("
|
|
f"id={self.id}, "
|
|
f"store_name='{self.store_name}', "
|
|
f"task_id='{truncate(self.task_id)}'"
|
|
f")>"
|
|
)
|
|
|
|
|
|
class Image(Base):
|
|
"""
|
|
업로드 이미지 테이블
|
|
|
|
사용자가 업로드한 이미지의 URL을 저장합니다.
|
|
독립적으로 관리되며 Project와 직접적인 관계가 없습니다.
|
|
|
|
Attributes:
|
|
id: 고유 식별자 (자동 증가)
|
|
task_id: 이미지 업로드 작업 고유 식별자 (UUID7)
|
|
img_name: 이미지명
|
|
img_url: 이미지 URL (S3, CDN 등의 경로)
|
|
created_at: 생성 일시 (자동 설정)
|
|
"""
|
|
|
|
__tablename__ = "image"
|
|
__table_args__ = (
|
|
Index("idx_image_task_id", "task_id"),
|
|
Index("idx_image_is_deleted", "is_deleted"),
|
|
{
|
|
"mysql_engine": "InnoDB",
|
|
"mysql_charset": "utf8mb4",
|
|
"mysql_collate": "utf8mb4_unicode_ci",
|
|
},
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(
|
|
Integer,
|
|
primary_key=True,
|
|
nullable=False,
|
|
autoincrement=True,
|
|
comment="고유 식별자",
|
|
)
|
|
|
|
task_id: Mapped[str] = mapped_column(
|
|
String(36),
|
|
nullable=False,
|
|
comment="이미지 업로드 작업 고유 식별자 (UUID7)",
|
|
)
|
|
|
|
img_name: Mapped[str] = mapped_column(
|
|
String(255),
|
|
nullable=False,
|
|
comment="이미지명",
|
|
)
|
|
|
|
img_url: Mapped[str] = mapped_column(
|
|
String(2048),
|
|
nullable=False,
|
|
comment="이미지 URL (blob, CDN 경로)",
|
|
)
|
|
|
|
img_order: Mapped[int] = mapped_column(
|
|
Integer,
|
|
nullable=False,
|
|
default=0,
|
|
comment="이미지 순서",
|
|
)
|
|
|
|
is_deleted: Mapped[bool] = mapped_column(
|
|
Boolean,
|
|
nullable=False,
|
|
default=False,
|
|
comment="소프트 삭제 여부 (True: 삭제됨)",
|
|
)
|
|
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime,
|
|
nullable=False,
|
|
server_default=func.now(),
|
|
comment="생성 일시",
|
|
)
|
|
|
|
def __repr__(self) -> str:
|
|
task_id_str = (
|
|
(self.task_id[:10] + "...") if len(self.task_id) > 10 else self.task_id
|
|
)
|
|
img_name_str = (
|
|
(self.img_name[:10] + "...") if len(self.img_name) > 10 else self.img_name
|
|
)
|
|
|
|
return (
|
|
f"<Image(id={self.id}, task_id='{task_id_str}', img_name='{img_name_str}')>"
|
|
)
|