# models/database/media.py """ Media file models for vendor media library. This module provides: - MediaFile: Vendor-uploaded media files (images, documents, videos) - ProductMedia: Many-to-many relationship between products and media Files are stored in vendor-specific directories: uploads/vendors/{vendor_id}/{folder}/{filename} """ from datetime import datetime from sqlalchemy import ( Boolean, Column, DateTime, ForeignKey, Index, Integer, String, Text, UniqueConstraint, ) from sqlalchemy.dialects.sqlite import JSON from sqlalchemy.orm import relationship from app.core.database import Base from models.database.base import TimestampMixin class MediaFile(Base, TimestampMixin): """Vendor media file record. Stores metadata about uploaded files. Actual files are stored in the filesystem at uploads/vendors/{vendor_id}/{folder}/ """ __tablename__ = "media_files" id = Column(Integer, primary_key=True, index=True) vendor_id = Column(Integer, ForeignKey("vendors.id"), nullable=False) # File identification filename = Column(String(255), nullable=False) # Stored filename (UUID-based) original_filename = Column(String(255)) # Original uploaded filename file_path = Column(String(500), nullable=False) # Relative path from uploads/ # File properties media_type = Column(String(20), nullable=False) # image, video, document mime_type = Column(String(100)) file_size = Column(Integer) # bytes # Image/video dimensions width = Column(Integer) height = Column(Integer) # Thumbnail (for images/videos) thumbnail_path = Column(String(500)) # Metadata alt_text = Column(String(500)) description = Column(Text) folder = Column(String(100), default="general") # products, general, etc. tags = Column(JSON) # List of tags for categorization extra_metadata = Column(JSON) # Additional metadata (EXIF, etc.) # Status is_optimized = Column(Boolean, default=False) optimized_size = Column(Integer) # Size after optimization # Usage tracking usage_count = Column(Integer, default=0) # How many times used # Relationships vendor = relationship("Vendor", back_populates="media_files") product_associations = relationship( "ProductMedia", back_populates="media", cascade="all, delete-orphan", ) __table_args__ = ( Index("idx_media_vendor_id", "vendor_id"), Index("idx_media_vendor_folder", "vendor_id", "folder"), Index("idx_media_vendor_type", "vendor_id", "media_type"), Index("idx_media_filename", "filename"), ) def __repr__(self): return ( f"" ) @property def file_url(self) -> str: """Get the public URL for this file.""" return f"/uploads/{self.file_path}" @property def thumbnail_url(self) -> str | None: """Get the thumbnail URL if available.""" if self.thumbnail_path: return f"/uploads/{self.thumbnail_path}" return None @property def is_image(self) -> bool: """Check if this is an image file.""" return self.media_type == "image" @property def is_video(self) -> bool: """Check if this is a video file.""" return self.media_type == "video" @property def is_document(self) -> bool: """Check if this is a document file.""" return self.media_type == "document" class ProductMedia(Base, TimestampMixin): """Association between products and media files. Tracks which media files are used by which products, including the usage type (main image, gallery, variant, etc.) """ __tablename__ = "product_media" id = Column(Integer, primary_key=True, index=True) product_id = Column( Integer, ForeignKey("products.id", ondelete="CASCADE"), nullable=False, ) media_id = Column( Integer, ForeignKey("media_files.id", ondelete="CASCADE"), nullable=False, ) # Usage type usage_type = Column(String(50), nullable=False, default="gallery") # Types: main_image, gallery, variant, thumbnail, swatch # Display order for galleries display_order = Column(Integer, default=0) # Variant-specific (if usage_type is variant) variant_id = Column(Integer) # Reference to variant if applicable # Relationships product = relationship("Product") media = relationship("MediaFile", back_populates="product_associations") __table_args__ = ( UniqueConstraint( "product_id", "media_id", "usage_type", name="uq_product_media_usage" ), Index("idx_product_media_product", "product_id"), Index("idx_product_media_media", "media_id"), ) def __repr__(self): return ( f"" )