# models/database/content_page.py """ Content Page Model Manages static content pages (About, FAQ, Contact, Shipping, Returns, etc.) with platform-level defaults and vendor-specific overrides. Features: - Platform-level default content - Vendor-specific overrides - Rich text content (HTML/Markdown) - SEO metadata - Published/Draft status - Version history support """ from datetime import datetime, timezone from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text, UniqueConstraint, Index from sqlalchemy.orm import relationship from app.core.database import Base class ContentPage(Base): """ Content pages for shops (About, FAQ, Contact, etc.) Two-tier system: 1. Platform-level defaults (vendor_id=NULL) 2. Vendor-specific overrides (vendor_id=123) Lookup logic: 1. Check for vendor-specific page (vendor_id + slug) 2. If not found, use platform default (slug only) 3. If neither exists, show 404 or default template """ __tablename__ = "content_pages" id = Column(Integer, primary_key=True, index=True) # Vendor association (NULL = platform default) vendor_id = Column(Integer, ForeignKey("vendors.id", ondelete="CASCADE"), nullable=True, index=True) # Page identification slug = Column(String(100), nullable=False, index=True) # about, faq, contact, shipping, returns, etc. title = Column(String(200), nullable=False) # Content content = Column(Text, nullable=False) # HTML or Markdown content_format = Column(String(20), default="html") # html, markdown # Template selection (for landing pages) # Options: 'default', 'minimal', 'modern', 'full' # Only used for landing pages (slug='landing' or 'home') template = Column(String(50), default="default", nullable=False) # SEO meta_description = Column(String(300), nullable=True) meta_keywords = Column(String(300), nullable=True) # Publishing is_published = Column(Boolean, default=False, nullable=False) published_at = Column(DateTime(timezone=True), nullable=True) # Ordering (for menus, footers) display_order = Column(Integer, default=0) show_in_footer = Column(Boolean, default=True) show_in_header = Column(Boolean, default=False) # Timestamps created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False) updated_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False) # Author tracking (admin or vendor user who created/updated) created_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True) updated_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True) # Relationships vendor = relationship("Vendor", back_populates="content_pages") creator = relationship("User", foreign_keys=[created_by]) updater = relationship("User", foreign_keys=[updated_by]) # Constraints __table_args__ = ( # Unique combination: vendor can only have one page per slug # Platform defaults (vendor_id=NULL) can only have one page per slug UniqueConstraint('vendor_id', 'slug', name='uq_vendor_slug'), # Indexes for performance Index('idx_vendor_published', 'vendor_id', 'is_published'), Index('idx_slug_published', 'slug', 'is_published'), ) def __repr__(self): vendor_name = self.vendor.name if self.vendor else "PLATFORM" return f"" @property def is_platform_default(self): """Check if this is a platform-level default page.""" return self.vendor_id is None @property def is_vendor_override(self): """Check if this is a vendor-specific override.""" return self.vendor_id is not None def to_dict(self): """Convert to dictionary for API responses.""" return { "id": self.id, "vendor_id": self.vendor_id, "vendor_name": self.vendor.name if self.vendor else None, "slug": self.slug, "title": self.title, "content": self.content, "content_format": self.content_format, "template": self.template, "meta_description": self.meta_description, "meta_keywords": self.meta_keywords, "is_published": self.is_published, "published_at": self.published_at.isoformat() if self.published_at else None, "display_order": self.display_order, "show_in_footer": self.show_in_footer, "show_in_header": self.show_in_header, "is_platform_default": self.is_platform_default, "is_vendor_override": self.is_vendor_override, "created_at": self.created_at.isoformat() if self.created_at else None, "updated_at": self.updated_at.isoformat() if self.updated_at else None, "created_by": self.created_by, "updated_by": self.updated_by, } # Add relationship to Vendor model # This should be added to models/database/vendor.py: # content_pages = relationship("ContentPage", back_populates="vendor", cascade="all, delete-orphan")