forked from andika/membership-be
Prod Deployment Preparation
This commit is contained in:
259
alembic/README.md
Normal file
259
alembic/README.md
Normal file
@@ -0,0 +1,259 @@
|
||||
# Alembic Database Migrations
|
||||
|
||||
This directory contains **Alembic** database migrations for the LOAF membership platform.
|
||||
|
||||
## What is Alembic?
|
||||
|
||||
Alembic is a lightweight database migration tool for SQLAlchemy. It allows you to:
|
||||
- Track database schema changes over time
|
||||
- Apply migrations incrementally
|
||||
- Roll back changes if needed
|
||||
- Auto-generate migration scripts from model changes
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
alembic/
|
||||
├── versions/ # Migration scripts (KEEP IN VERSION CONTROL)
|
||||
│ └── *.py # Individual migration files
|
||||
├── env.py # Alembic environment configuration
|
||||
├── script.py.mako # Template for new migration files
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Create a New Migration
|
||||
|
||||
After making changes to `models.py`, generate a migration:
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
alembic revision --autogenerate -m "add_user_bio_field"
|
||||
```
|
||||
|
||||
This will create a new file in `alembic/versions/` like:
|
||||
```
|
||||
3e02c74581c9_add_user_bio_field.py
|
||||
```
|
||||
|
||||
### 2. Review the Generated Migration
|
||||
|
||||
**IMPORTANT:** Always review auto-generated migrations before applying them!
|
||||
|
||||
```bash
|
||||
# Open the latest migration file
|
||||
cat alembic/versions/3e02c74581c9_add_user_bio_field.py
|
||||
```
|
||||
|
||||
Check:
|
||||
- ✅ The `upgrade()` function contains the correct changes
|
||||
- ✅ The `downgrade()` function properly reverses those changes
|
||||
- ✅ No unintended table drops or data loss
|
||||
|
||||
### 3. Apply the Migration
|
||||
|
||||
```bash
|
||||
# Apply all pending migrations
|
||||
alembic upgrade head
|
||||
|
||||
# Or apply migrations one at a time
|
||||
alembic upgrade +1
|
||||
```
|
||||
|
||||
### 4. Rollback a Migration
|
||||
|
||||
```bash
|
||||
# Rollback the last migration
|
||||
alembic downgrade -1
|
||||
|
||||
# Rollback to a specific revision
|
||||
alembic downgrade 3e02c74581c9
|
||||
```
|
||||
|
||||
## Common Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `alembic current` | Show current migration revision |
|
||||
| `alembic history` | Show migration history |
|
||||
| `alembic heads` | Show head revisions |
|
||||
| `alembic upgrade head` | Apply all pending migrations |
|
||||
| `alembic downgrade -1` | Rollback last migration |
|
||||
| `alembic revision --autogenerate -m "message"` | Create new migration |
|
||||
| `alembic stamp head` | Mark database as up-to-date without running migrations |
|
||||
|
||||
## Migration Workflow
|
||||
|
||||
### For Development
|
||||
|
||||
1. **Make changes to `models.py`**
|
||||
```python
|
||||
# In models.py
|
||||
class User(Base):
|
||||
# ...existing fields...
|
||||
bio = Column(Text, nullable=True) # New field
|
||||
```
|
||||
|
||||
2. **Generate migration**
|
||||
```bash
|
||||
alembic revision --autogenerate -m "add_user_bio_field"
|
||||
```
|
||||
|
||||
3. **Review the generated file**
|
||||
```python
|
||||
# In alembic/versions/xxxxx_add_user_bio_field.py
|
||||
def upgrade():
|
||||
op.add_column('users', sa.Column('bio', sa.Text(), nullable=True))
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('users', 'bio')
|
||||
```
|
||||
|
||||
4. **Apply migration**
|
||||
```bash
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
5. **Commit migration file to Git**
|
||||
```bash
|
||||
git add alembic/versions/xxxxx_add_user_bio_field.py
|
||||
git commit -m "Add user bio field"
|
||||
```
|
||||
|
||||
### For Production Deployment
|
||||
|
||||
**Fresh Database (New Installation):**
|
||||
```bash
|
||||
# 1. Create database
|
||||
createdb membership_db
|
||||
|
||||
# 2. Run initial schema SQL (creates all 17 tables)
|
||||
psql -U username -d membership_db -f ../migrations/000_initial_schema.sql
|
||||
|
||||
# 3. Mark database as up-to-date with Alembic
|
||||
alembic stamp head
|
||||
|
||||
# 4. Verify
|
||||
alembic current # Should show: 001_initial_baseline (head)
|
||||
```
|
||||
|
||||
**Existing Database (Apply New Migrations):**
|
||||
```bash
|
||||
# 1. Pull latest code
|
||||
git pull origin main
|
||||
|
||||
# 2. Apply migrations
|
||||
alembic upgrade head
|
||||
|
||||
# 3. Verify
|
||||
alembic current
|
||||
|
||||
# 4. Restart application
|
||||
systemctl restart membership-backend
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Database Connection
|
||||
|
||||
Alembic reads the `DATABASE_URL` from your `.env` file:
|
||||
|
||||
```env
|
||||
DATABASE_URL=postgresql://user:password@localhost:5432/membership_db
|
||||
```
|
||||
|
||||
The connection is configured in `alembic/env.py` (lines 29-36).
|
||||
|
||||
### Target Metadata
|
||||
|
||||
Alembic uses `Base.metadata` from `models.py` to detect changes:
|
||||
|
||||
```python
|
||||
# In alembic/env.py
|
||||
from models import Base
|
||||
target_metadata = Base.metadata
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
### ✅ DO:
|
||||
- Always review auto-generated migrations before applying
|
||||
- Test migrations in development before production
|
||||
- Commit migration files to version control
|
||||
- Write descriptive migration messages
|
||||
- Include both `upgrade()` and `downgrade()` functions
|
||||
|
||||
### ❌ DON'T:
|
||||
- Don't edit migration files after they've been applied in production
|
||||
- Don't delete migration files from `alembic/versions/`
|
||||
- Don't modify the `revision` or `down_revision` values
|
||||
- Don't commit `.pyc` files (already in .gitignore)
|
||||
|
||||
## Migration History
|
||||
|
||||
| Revision | Description | Date | Type |
|
||||
|----------|-------------|------|------|
|
||||
| `001_initial_baseline` | Baseline marker (empty migration) | 2026-01-02 | Baseline |
|
||||
|
||||
**Note:** The actual initial schema is created by running `backend/migrations/000_initial_schema.sql`. The baseline migration is an empty marker that indicates the starting point for Alembic tracking.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Target database is not up to date"
|
||||
|
||||
```bash
|
||||
# Check current revision
|
||||
alembic current
|
||||
|
||||
# Check pending migrations
|
||||
alembic history
|
||||
|
||||
# Apply missing migrations
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
### "FAILED: Can't locate revision identified by 'xxxxx'"
|
||||
|
||||
The database thinks it's at a revision that doesn't exist in your `alembic/versions/`.
|
||||
|
||||
**Solution:**
|
||||
```bash
|
||||
# Mark database at a known good revision
|
||||
alembic stamp head
|
||||
```
|
||||
|
||||
### Migration conflicts
|
||||
|
||||
If you get merge conflicts in migration files:
|
||||
|
||||
1. Resolve conflicts in the migration file
|
||||
2. Ensure `revision` and `down_revision` chain is correct
|
||||
3. Test the migration locally
|
||||
|
||||
### Fresh database setup
|
||||
|
||||
For a completely new database:
|
||||
|
||||
```bash
|
||||
# Step 1: Run initial schema SQL
|
||||
psql -U username -d membership_db -f ../migrations/000_initial_schema.sql
|
||||
|
||||
# Step 2: Mark as up-to-date
|
||||
alembic stamp head
|
||||
|
||||
# Step 3: Verify
|
||||
alembic current # Should show: 001_initial_baseline (head)
|
||||
```
|
||||
|
||||
## Legacy Migrations
|
||||
|
||||
Old numbered SQL migrations (`000_initial_schema.sql` through `011_wordpress_import_enhancements.sql`) are preserved in `backend/migrations/` for reference. These have been consolidated into the initial Alembic migration.
|
||||
|
||||
**Going forward, all new migrations must use Alembic.**
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Alembic Documentation](https://alembic.sqlalchemy.org/)
|
||||
- [SQLAlchemy Documentation](https://docs.sqlalchemy.org/)
|
||||
- [PostgreSQL Documentation](https://www.postgresql.org/docs/)
|
||||
Reference in New Issue
Block a user