Spaces:
Sleeping
Sleeping
Database Safety Guide
β οΈ CRITICAL: Database Management Rules
Tables Are Managed in Supabase
- ALL tables are created and managed in Supabase via
docs/schema/schema.sql - SQLAlchemy models ONLY map to existing tables - they do NOT create tables
- Alembic migrations are DISABLED for automatic execution
Why This Approach?
- Safety - Prevents accidental table creation/modification in production
- Control - All schema changes reviewed and applied manually in Supabase
- Audit Trail - Supabase tracks all schema changes
- Rollback - Easy to rollback changes in Supabase dashboard
How to Make Schema Changes
β NEVER DO THIS:
# DON'T call this in production code!
Base.metadata.create_all(bind=engine)
β CORRECT PROCESS:
Update schema.sql
-- Add new column to users table ALTER TABLE users ADD COLUMN new_field TEXT;Test in Supabase Dashboard
- Go to SQL Editor
- Run the ALTER statement
- Verify it works
Update SQLAlchemy Model
class User(BaseModel): __tablename__ = "users" new_field = Column(String(255), nullable=True)Deploy Code
- Models now map to updated table
- No automatic migrations run
Alembic Configuration
Alembic is configured but NOT used for automatic migrations.
Current setup:
alembic.ini- Points to environment variable (not hardcoded)alembic/versions/- Empty (no migration files)- Migrations must be run manually after review
If you need to use Alembic:
# 1. Create migration (review it first!)
alembic revision --autogenerate -m "description"
# 2. Review the generated migration file
# Check: alembic/versions/xxx_description.py
# 3. Test in development first
alembic upgrade head
# 4. If safe, run in production (manually)
Model Mapping Rules
Use
__tablename__- Always specify snake_case table nameclass User(BaseModel): __tablename__ = "users" # β Matches Supabase tableMatch column names exactly - Use snake_case
first_name = Column(String(100)) # β Matches DB column firstName = Column(String(100)) # β Won't workMatch data types - Use correct SQLAlchemy types
# Supabase: UUID id = Column(UUID(as_uuid=True)) # β # Supabase: JSONB metadata = Column(JSONB, default={}) # β # Supabase: TIMESTAMP WITH TIME ZONE created_at = Column(DateTime(timezone=True)) # βDon't define relationships yet - Keep models simple
# β Avoid for now (adds complexity) # tickets = relationship("Ticket", back_populates="user") # β Use queries instead # tickets = db.query(Ticket).filter(Ticket.user_id == user.id).all()
Checking Table Structure
In Supabase Dashboard:
- Go to Table Editor
- Select table
- View columns, types, constraints
In Code:
from sqlalchemy import inspect
inspector = inspect(engine)
columns = inspector.get_columns('users')
print(columns)
Current Table Status
All 41 tables exist in Supabase:
- β users
- β clients
- β contractors
- β projects
- β tickets
- β (36 more tables...)
See docs/schema/schema.sql for complete schema.
Safety Checklist
Before deploying code:
- No
Base.metadata.create_all()calls in code - No automatic Alembic migrations in startup
- Models use correct
__tablename__ - Column names match Supabase (snake_case)
- Data types match Supabase schema
- Tested queries work with existing tables
Emergency: If Tables Get Created Accidentally
If code accidentally creates duplicate tables:
- Stop the application immediately
- Check Supabase logs - See what was created
- Drop duplicate tables (if any)
DROP TABLE IF EXISTS duplicate_table_name; - Fix the code - Remove table creation logic
- Redeploy
Questions?
- Schema questions β Check
docs/schema/schema.sql - Model questions β Check
src/app/models/ - Migration questions β Ask before running!