Миграции — это система контроля версий для структуры базы данных, позволяющая безопасно вносить и откатывать изменения. Рассмотрим их реализацию в Ruby-экосистеме.
Миграция представляет собой:
class CreateProducts < ActiveRecord::Migration[7.0]
def change
create_table :products do |t|
t.string :name
t.text :description
t.decimal :price, precision: 8, scale: 2
t.timestamps
end
end
end
rails generate migration AddCategoryToProducts category:references
Команда | Действие |
---|---|
rails db:migrate | Применяет все новые миграции |
rails db:rollback | Откатывает последнюю миграцию |
rails db:migrate:status | Показывает статус миграций |
rails db:migrate:redo | Перезапускает последнюю миграцию |
rails db:schema:load | Загружает схему из schema.rb |
def change
add_column :users, :age, :integer
add_index :users, :email, unique: true
change_column_null :posts, :title, false
end
def up
create_table :categories do |t|
t.string :name
end
end
def down
drop_table :categories
end
def change
if table_exists?(:old_products)
rename_table :old_products, :products
end
end
def change
reversible do |dir|
dir.up do
# Код для применения миграции
end
dir.down do
# Код для отката миграции
end
end
end
def up
add_column :users, :new_flag, :boolean, default: false
User.in_batches do |batch|
batch.update_all(new_flag: true)
end
end
class BackfillUserStatus < ActiveRecord::Migration[7.0]
def change
# Только добавляет колонку
add_column :users, :status, :string
# Фактическое заполнение выносится в фоновую задачу
reversible do |dir|
dir.up { BackfillUserStatusJob.perform_later }
end
end
end
def change
User.where(role: nil).update_all(role: 'guest')
# Лучше через find_each для больших таблиц
User.where(active: nil).find_each do |user|
user.update(active: true)
end
end
class AddDefaultStatus < ActiveRecord::Migration[7.0]
class TemporaryUser < ApplicationRecord
self.table_name = 'users'
end
def change
TemporaryUser.where(status: nil).update_all(status: 'pending')
end
end
class AddIndexToUsersEmail < ActiveRecord::Migration[7.0]
disable_ddl_transaction! # Для Postgres позволяет избежать блокировок
def change
add_index :users, :email, algorithm: :concurrently, unique: true
rescue ActiveRecord::RecordNotUnique
# Обработка случая, когда есть дубликаты email
remove_index :users, :email, algorithm: :concurrently
execute "CREATE INDEX CONCURRENTLY index_users_on_email ON users (lower(email))"
end
end
ActiveRecord::Migration.verbose = true
ActiveRecord::Base.logger = Logger.new(STDOUT)
Для Postgres можно добавить:
execute "SELECT pg_sleep(0.1)" if Rails.env.development?
Миграции БД в Ruby-приложениях:
Правильное использование миграций позволяет: