refactor: migrate Feature to billing module and split ProductMedia to catalog
- Move Feature model from models/database/ to app/modules/billing/models/ (tightly coupled to SubscriptionTier for tier-based access control) - Move ProductMedia from models/database/media.py to app/modules/catalog/models/ (product-specific media associations belong with catalog) - Keep MediaFile as CORE in models/database/media.py (cross-cutting file storage) - Convert legacy feature.py to re-export for backwards compatibility - Update all imports to use canonical module locations Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,27 +1,25 @@
|
||||
# models/database/media.py
|
||||
"""
|
||||
Media file models for vendor media library.
|
||||
CORE media file model for vendor media library.
|
||||
|
||||
This module provides:
|
||||
- MediaFile: Vendor-uploaded media files (images, documents, videos)
|
||||
- ProductMedia: Many-to-many relationship between products and media
|
||||
This is a CORE framework model used across multiple modules.
|
||||
MediaFile provides vendor-uploaded media files (images, documents, videos).
|
||||
|
||||
For product-media associations, use:
|
||||
from app.modules.catalog.models import ProductMedia
|
||||
|
||||
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
|
||||
@@ -75,6 +73,7 @@ class MediaFile(Base, TimestampMixin):
|
||||
|
||||
# Relationships
|
||||
vendor = relationship("Vendor", back_populates="media_files")
|
||||
# ProductMedia relationship uses string reference to avoid circular import
|
||||
product_associations = relationship(
|
||||
"ProductMedia",
|
||||
back_populates="media",
|
||||
@@ -122,52 +121,7 @@ class MediaFile(Base, TimestampMixin):
|
||||
return self.media_type == "document"
|
||||
|
||||
|
||||
class ProductMedia(Base, TimestampMixin):
|
||||
"""Association between products and media files.
|
||||
# Re-export ProductMedia from its canonical location for backwards compatibility
|
||||
from app.modules.catalog.models import ProductMedia # noqa: E402, F401
|
||||
|
||||
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"<ProductMedia(product_id={self.product_id}, "
|
||||
f"media_id={self.media_id}, usage='{self.usage_type}')>"
|
||||
)
|
||||
__all__ = ["MediaFile", "ProductMedia"]
|
||||
|
||||
Reference in New Issue
Block a user