"""Playlist-Track association model.""" import uuid from datetime import datetime from typing import TYPE_CHECKING from sqlalchemy import Integer, ForeignKey, UniqueConstraint from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.database import Base if TYPE_CHECKING: from app.models.playlist import Playlist from app.models.track import Track from app.models.user import User class PlaylistTrack(Base): """Association model for Playlist-Track many-to-many relationship.""" __tablename__ = "playlist_tracks" __table_args__ = ( UniqueConstraint("playlist_id", "position", name="uq_playlist_position"), ) # Primary key id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True, ) # Foreign keys playlist_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("playlists.id", ondelete="CASCADE"), nullable=False, index=True, ) track_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("tracks.id", ondelete="CASCADE"), nullable=False, index=True, ) # Position in playlist (starts at 0) position: Mapped[int] = mapped_column( Integer, nullable=False, ) # User who added this track to playlist added_by: Mapped[uuid.UUID | None] = mapped_column( UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True, ) # Timestamp when track was added added_at: Mapped[datetime] = mapped_column( default=datetime.utcnow, nullable=False, ) # Relationships playlist: Mapped["Playlist"] = relationship( "Playlist", back_populates="playlist_tracks", lazy="selectin", ) track: Mapped["Track"] = relationship( "Track", lazy="selectin", ) added_by_user: Mapped["User"] = relationship( "User", back_populates="added_playlist_tracks", lazy="selectin", foreign_keys=[added_by], ) def __repr__(self) -> str: return f"" def to_dict(self) -> dict: """Convert playlist-track model to dictionary.""" return { "id": str(self.id), "playlist_id": str(self.playlist_id), "track_id": str(self.track_id), "position": self.position, "added_by": str(self.added_by) if self.added_by else None, "added_at": self.added_at.isoformat(), }