feat: add CMS database model and migrations
Implement Content Management System database layer: Database Model: - ContentPage model with two-tier architecture - Platform defaults (vendor_id=NULL) - Vendor-specific overrides (vendor_id=123) - SEO fields (meta_description, meta_keywords) - Publishing workflow (is_published, published_at) - Navigation flags (show_in_footer, show_in_header) - Display ordering and timestamps Migrations: - Create content_pages table with all columns - Add indexes for performance (vendor_id, slug, published status) - Add unique constraint on (vendor_id, slug) - Add foreign key relationships with cascade delete Model Registration: - Add ContentPage to Vendor relationship - Import model in alembic/env.py for migration detection This provides the foundation for managing static content pages (About, FAQ, Contact, etc.) with platform defaults and vendor overrides. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -93,6 +93,16 @@ try:
|
||||
except ImportError as e:
|
||||
print(f" ✗ VendorTheme model failed: {e}")
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# CONTENT PAGE MODEL (CMS)
|
||||
# ----------------------------------------------------------------------------
|
||||
try:
|
||||
from models.database.content_page import ContentPage
|
||||
|
||||
print(" ✓ ContentPage model imported")
|
||||
except ImportError as e:
|
||||
print(f" ✗ ContentPage model failed: {e}")
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# PRODUCT MODELS
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
"""Ensure content_pages table with all columns
|
||||
|
||||
Revision ID: 72aa309d4007
|
||||
Revises: fef1d20ce8b4
|
||||
Create Date: 2025-11-22 15:16:13.213613
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '72aa309d4007'
|
||||
down_revision: Union[str, None] = 'fef1d20ce8b4'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('content_pages',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('vendor_id', sa.Integer(), nullable=True),
|
||||
sa.Column('slug', sa.String(length=100), nullable=False),
|
||||
sa.Column('title', sa.String(length=200), nullable=False),
|
||||
sa.Column('content', sa.Text(), nullable=False),
|
||||
sa.Column('content_format', sa.String(length=20), nullable=True),
|
||||
sa.Column('meta_description', sa.String(length=300), nullable=True),
|
||||
sa.Column('meta_keywords', sa.String(length=300), nullable=True),
|
||||
sa.Column('is_published', sa.Boolean(), nullable=False),
|
||||
sa.Column('published_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column('display_order', sa.Integer(), nullable=True),
|
||||
sa.Column('show_in_footer', sa.Boolean(), nullable=True),
|
||||
sa.Column('show_in_header', sa.Boolean(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('created_by', sa.Integer(), nullable=True),
|
||||
sa.Column('updated_by', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['created_by'], ['users.id'], ondelete='SET NULL'),
|
||||
sa.ForeignKeyConstraint(['updated_by'], ['users.id'], ondelete='SET NULL'),
|
||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('vendor_id', 'slug', name='uq_vendor_slug')
|
||||
)
|
||||
op.create_index('idx_slug_published', 'content_pages', ['slug', 'is_published'], unique=False)
|
||||
op.create_index('idx_vendor_published', 'content_pages', ['vendor_id', 'is_published'], unique=False)
|
||||
op.create_index(op.f('ix_content_pages_id'), 'content_pages', ['id'], unique=False)
|
||||
op.create_index(op.f('ix_content_pages_slug'), 'content_pages', ['slug'], unique=False)
|
||||
op.create_index(op.f('ix_content_pages_vendor_id'), 'content_pages', ['vendor_id'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_content_pages_vendor_id'), table_name='content_pages')
|
||||
op.drop_index(op.f('ix_content_pages_slug'), table_name='content_pages')
|
||||
op.drop_index(op.f('ix_content_pages_id'), table_name='content_pages')
|
||||
op.drop_index('idx_vendor_published', table_name='content_pages')
|
||||
op.drop_index('idx_slug_published', table_name='content_pages')
|
||||
op.drop_table('content_pages')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,34 @@
|
||||
"""Add content_pages table for CMS
|
||||
|
||||
Revision ID: fef1d20ce8b4
|
||||
Revises: fa7d4d10e358
|
||||
Create Date: 2025-11-22 13:41:18.069674
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'fef1d20ce8b4'
|
||||
down_revision: Union[str, None] = 'fa7d4d10e358'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index('idx_roles_vendor_name', table_name='roles')
|
||||
op.drop_index('idx_vendor_users_invitation_token', table_name='vendor_users')
|
||||
op.create_index(op.f('ix_vendor_users_invitation_token'), 'vendor_users', ['invitation_token'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_vendor_users_invitation_token'), table_name='vendor_users')
|
||||
op.create_index('idx_vendor_users_invitation_token', 'vendor_users', ['invitation_token'], unique=False)
|
||||
op.create_index('idx_roles_vendor_name', 'roles', ['vendor_id', 'name'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
Reference in New Issue
Block a user