function up() {
transaction {
// Check if column exists
if (!hasColumn("users", "avatar")) {
addColumn(table="users", column="avatar", type="string");
}
// Check if table exists
if (!hasTable("analytics")) {
t = createTable("analytics");
t.integer("views");
t.timestamps();
t.create();
}
// Database-specific
if (getDatabaseType() == "mysql") {
sql("ALTER TABLE users ENGINE=InnoDB");
}
}
}
Using Raw SQL
function up() {
transaction {
// Complex operations
sql("
CREATE VIEW active_products AS
SELECT * FROM products
WHERE active = 1 AND deletedAt IS NULL
");
// Stored procedures
sql("
CREATE PROCEDURE CleanupOldData()
BEGIN
DELETE FROM logs WHERE createdAt < DATE_SUB(NOW(), INTERVAL 90 DAY);
END
");
}
}
Environment-Specific
function up() {
transaction {
// Always run
addColumn(table="users", column="lastLoginAt", type="datetime");
// Development only
if (getEnvironment() == "development") {
// Add test data
for (var i = 1; i <= 100; i++) {
addRecord(
table="users",
email="test#i#@example.com",
password="hashed_password"
);
}
}
}
}
Running Migrations
Basic Commands
# Check migration status
wheels dbmigrate info
# Run all pending migrations
wheels dbmigrate latest
# Run next migration only
wheels dbmigrate up
# Rollback last migration
wheels dbmigrate down
# Run specific version
wheels dbmigrate exec 20240125143022
# Reset all migrations
wheels dbmigrate reset
# Test up
wheels dbmigrate latest
# Test down
wheels dbmigrate down
# Test up again
wheels dbmigrate up
5. Never Modify Completed Migrations
# Bad: Editing existing migration
# Good: Create new migration to fix issues
wheels dbmigrate create blank fix_orders_status_column
Common Patterns
Adding Non-Nullable Column
function up() {
transaction {
// Add nullable first
addColumn(table="users", column="role", type="string", null=true);
// Set default values
updateRecord(table="users", where="1=1", values={role: "member"});
// Make non-nullable
changeColumn(table="users", column="role", null=false);
}
}
Renaming Table with Foreign Keys
function up() {
transaction {
// Drop foreign keys first
removeForeignKey(table="posts", name="fk_posts_users");
// Rename table
renameTable(oldName="posts", newName="articles");
// Recreate foreign keys
addForeignKey(
table="articles",
column="userId",
referenceTable="users",
referenceColumn="id"
);
}
}
Safe Column Removal
function up() {
transaction {
// First migration: deprecate column
if (getEnvironment() != "production") {
announce("Column 'users.oldField' is deprecated and will be removed");
}
}
}
// Later migration (after code deployment)
function up() {
transaction {
removeColumn(table="users", column="oldField");
}
}
-- Manually fix schema_migrations table
DELETE FROM schema_migrations WHERE version = '20240125143022';
Performance Issues
function up() {
// Increase timeout for large tables
setting requestTimeout="300";
transaction {
// Add index without locking (MySQL)
sql("ALTER TABLE large_table ADD INDEX idx_column (column)");
}
}
Integration with CI/CD
Pre-deployment Check
#!/bin/bash
# Check for pending migrations
if wheels dbmigrate info | grep -q "pending"; then
echo "⚠️ Pending migrations detected!"
wheels dbmigrate info
exit 1
fi
Automated Deployment
# .github/workflows/deploy.yml
- name: Run migrations
run: |
wheels dbmigrate latest
wheels dbmigrate info