Railsの移行を分析する
今日の投稿では、Railsの移行について詳しく説明します。移行をさまざまな部分に分解し、その過程で、効果的な移行を作成する方法を学びます。複数のデータベースの移行を作成する方法、失敗した移行を処理する方法、およびロールバックを実行する手法について学習します。
投稿全体を理解するには、データベースとRailsの基本を理解している必要があります。
移行101
Railsでの移行により、アプリケーションの存続期間にわたってデータベースを進化させることができます。移行により、エレガントなDSLを提供することにより、データベースの状態を変更するためのプレーンなRubyコードを記述できます。移行により、データベースを操作し、DSLをデータベース固有のSQLクエリに変換する際の詳細を処理するための抽象化が提供されるため、データベース固有のSQLを作成する必要はありません。移行も邪魔にならず、そのような必要が生じた場合、データベースで生のSQLを実行する方法を提供します。
2万リーグからRailsデータベースへの移行
移行を使用して、テーブルの作成、列の追加または削除、列へのインデックスの追加を行うことができます。
すべてのRailsアプリには、
db / migrate
という特別なディレクトリがあります。 —すべての移行が保存される場所。
テーブルevents
を作成する移行から始めましょう データベースに追加します。
$ rails g migration CreateEvents category:string
このコマンドは、タイムスタンプ付きのファイル 20200405103635_create_events.rb
を生成します db / migrate
内 ディレクトリ。ファイルの内容は次のとおりです。
class CreateEvents < ActiveRecord::Migration[6.0]
def change
create_table :events do |t|
t.string :category
t.timestamps
end
end
end
この移行ファイルを分解してみましょう。
- Railsが生成するすべての移行ファイルには、ファイル名に存在するタイムスタンプがあります。このタイムスタンプは重要であり、後で説明するように、移行が実行されたかどうかを確認するためにRailsによって使用されます。
- 移行には、
ActiveRecord ::Migration [6.0]
から継承するクラスが含まれています 。 Rails 6を使用しているため、移行スーパークラスには[6.0]
があります。 。 Rails 5.2を使用していた場合、スーパークラスはActiveRecord ::Migration [5.2]
になります。 。後で、Railsバージョンがスーパークラス名の一部である理由について説明します。 - 移行には
change
メソッドがあります これには、データベースを操作するDSLコードが含まれています。この場合、change
メソッドはevents
を作成しています 列がcategory
のテーブル タイプstring
。 - 移行ではコード
t.timestamps
を使用します タイムスタンプを追加するにはcreated_at
およびupdated_at
events
へ テーブル。
この移行がrailsdb:migrate
を使用して実行される場合 コマンドを実行すると、 events
が作成されます カテゴリ
のテーブル タイプstring
の列 およびタイムスタンプ列created_at
およびupdated_at
。
実際のデータベースの列タイプは、データベースに応じてvarcharまたはtextになります。
移行タイムスタンプとschema_migrationテーブルの重要性
rails gmigration
を使用して移行が生成されるたび コマンドを実行すると、Railsは一意のタイムスタンプを持つ移行ファイルを生成します。タイムスタンプはYYYYMMDDHHMMSS
の形式です。 。移行が実行されるたびに、Railsは移行タイムスタンプを内部テーブル schema_migrations
に挿入します 。このテーブルは、最初の移行を実行するときにRailsによって作成されます。テーブルにはversion
列のみがあります 、これは主キーでもあります。これはschema_migrations
の構造です テーブル。
CREATE TABLE IF NOT EXISTS "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);
これで、 events
を作成するための移行が実行されました。 表では、Railsがこの移行のタイムスタンプを schema_migrations
に保存しているかどうかを確認しましょう。 テーブル。
sqlite> select * from schema_migrations;
20200405103635
移行を再度実行すると、Railsは最初に schema_migrations
にエントリが存在するかどうかを確認します。 移行ファイルのタイムスタンプを含むテーブル。そのようなエントリがない場合にのみ実行します。これにより、時間の経過とともにデータベースに変更を段階的に追加でき、移行はデータベースで1回だけ実行されます。
データベーススキーマ
ますます多くの移行を実行するにつれて、データベーススキーマは進化し続けます。 Railsは、最新のデータベーススキーマをファイル db / schema.rb
に保存します。 。このファイルは、アプリケーションの存続期間中にデータベースで実行されるすべての移行のRuby表現です。このファイルがあるため、古い移行ファイルをコードベースに保持する必要はありません。 Railsはdump
するタスクを提供します データベースからschema.rb
への最新のスキーマ およびload
schema.rb
からデータベースへのスキーマ 。そのため、古い移行をコードベースから安全に削除できます。また、データベースへのスキーマのロードは、アプリケーションをセットアップするたびにすべての移行を実行する場合に比べて高速です。
Railsは、データベーススキーマをSQL形式で保存する方法も提供します。 2つの形式を比較する記事がすでにあります。詳細については、こちらをご覧ください。
移行中のRailsバージョン
生成するすべての移行には、スーパークラスの一部としてRailsバージョンがあります。したがって、Rails 6アプリによって生成される移行には、スーパークラス ActiveRecord ::Migration [6.0]
があります。 一方、Rails 5.2アプリによって生成された移行には、スーパークラス ActiveRecord ::Migration [5.2]
があります。 。 Rails 4.2以下の古いアプリをお持ちの場合は、スーパークラスにバージョンがないことに気付くでしょう。スーパークラスは単なるActiveRecord::Migration
。
RailsバージョンがRails5の移行スーパークラスに追加されました。これにより、基本的に、古いバージョンのRailsによって生成された移行を中断することなく、migrationAPIを時間の経過とともに進化させることができます。
events
を作成するための同じ移行を見て、これをさらに詳しく見ていきましょう。 Rails4.2アプリのテーブル。
class CreateEvents < ActiveRecord::Migration
def change
create_table :events do |t|
t.string :category
t.timestamps null: false
end
end
end
events
のスキーマを見ると Rails 6の移行によって生成されたテーブルでは、 NOT NULL
であることがわかります。 タイムスタンプ列に制約があります。
sqlite> .schema events
CREATE TABLE IF NOT EXISTS "events" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "category" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
これは、Rails 5以降、移行APIが自動的に NOT NULL
を追加するためです。 移行ファイルに明示的に追加する必要のないタイムスタンプ列への制約。スーパークラス名のRailsバージョンは、移行が生成されたRailsバージョンの移行APIを使用することを保証します。これにより、Railsは古い移行との下位互換性を維持すると同時に、移行APIを進化させることができます。
データベーススキーマの変更
変更
メソッドは、移行の主要なメソッドです。移行が実行されると、 change
が呼び出されます。 メソッドを実行し、その中のコードを実行します。
create_table
と一緒に 、Railsは、もう1つの強力なメソッド change_table
も提供します。 。名前が示すように、既存のテーブルのスキーマを変更するために使用されます。
def change
change_table :events do |t|
t.remove :category
t.string :event_type
t.boolean :active, default: false
end
end
この移行により、 category
が削除されます events
の列 テーブルに、新しい文字列列 events_type
を追加します 新しいブール列active
デフォルト値はfalse
です 。
Railsは、次のような移行内で使用できる他の多くのヘルパーメソッドも提供します。
-
change_column
-
add_index
-
remove_index
-
rename_table
などなど。変更で使用できるすべての方法はここにあります
タイムスタンプ
t.timestamps
を見ました Railsによる移行に追加され、列 created_at
が追加されました およびupdated_at
events
へ テーブル。これらの特別な列は、レコードがいつ作成および更新されるかを追跡するためにRailsによって使用されます。Railsは、レコードが作成されるときにこれらの列に値を追加し、レコードが更新されるときに必ず更新します。これらの列は、データベースレコードの存続期間を追跡するのに役立ちます。
updated_at
updated_all
を実行しても、列は更新されません Railsのメソッド。
処理の失敗
移行は防弾ではありません。彼らは失敗する可能性があります。理由は、構文が間違っているか、データベースクエリが無効である可能性があります。理由が何であれ、データベースが不整合な状態にならないように、障害を処理して回復する必要があります。 Railsは、トランザクション内で各移行を実行することにより、この問題を解決します。移行が失敗した場合、トランザクションはロールバックされます。これにより、データベースが不整合な状態にならないようにします。
これは、データベーススキーマを更新するためのトランザクションをサポートするデータベースに対してのみ実行されます。これらは、データ定義言語(DDL)トランザクションとして知られています。 MySQLとPostgreSQLはどちらもDDLトランザクションをサポートしています。
トランザクション内で特定の移行を実行したくない場合があります。簡単な例は、PostgreSQLで同時インデックスを追加する場合です。このような移行は、PostgreSQLがテーブルのロックを取得せずにインデックスを追加しようとするため、DDLトランザクション内で実行できないため、データベースを停止せずに本番データベースにインデックスを追加できます。 Railsは、 disable_ddl_transactions!
の形式で、移行内のトランザクションをオプトアウトする方法を提供します。 。
def change
disable_ddl_transactions!
add_index :events, :user_id, algorithm: :concurrently
これにより、トランザクション内での移行は実行されません。このような移行が失敗した場合は、自分で回復する必要があります。この場合、 REINDEX
のいずれかを実行できます または、インデックスを削除して、もう一度追加してみてください。
リバーシブルマイグレーション
Railsを使用すると、次のコマンドを使用してデータベースへの変更をロールバックできます。
rails db:rollback
このコマンドは、データベースで実行された最後の移行を元に戻します。移行によって列event_type
が追加された場合 その後、ロールバックによってその列が削除されます。移行によってインデックスが追加された場合、ロールバックによってそのインデックスが削除されます。
以前の移行をロールバックして実行するためのコマンドもあります。
rails db:redo
です 。
Railsは、ほとんどの移行を元に戻す方法を知っているほど賢いです。ただし、 up
を提供することで、移行を元に戻す方法のヒントをRailsonに提供することもできます。 およびdown
change
を使用する代わりにメソッド メソッド。up
down
に対して、移行が実行されるときにメソッドが使用されます 移行がロールバックされるときにメソッドが使用されます。
def up
change_table :events do |t|
t.change :price, :string
end
end
def down
change_table :events do |t|
t.change :price, :integer
end
end
この例では、 price
を変更しています イベント
の列 integer
から string
へ 。 down
でロールバックする方法を指定します メソッド。
これと同じ移行は、 change
を使用して作成することもできます。 メソッド。
def change
reversible do |direction|
change_table :events do |t|
direction.up { t.change :price, :string }
direction.down { t.change :price, :integer }
end
end
end
Railsは、 revert
を使用して以前の移行を完全に元に戻す方法も提供します メソッド。
def change
revert CreateEvents
create_table :events do
...
end
end
元に戻すコード> メソッドは、移行を部分的に元に戻すためのブロックも受け入れます。
def change
revert do
reversible do |direction|
change_table :events do |t|
direction.up { t.remove :event_type }
direction.down { t.string :event_type }
end
end
end
end
生で実行する
移行内で複雑なSQLを実行したい場合があります。このような場合、通常の移行DSLを忘れて、代わりに次のように生のSQLを実行できます。
def change
execute <<-SQL
....
SQL
end
複数のデータベースと移行
Rails 6では、単一のRailsアプリケーション内で複数のデータベースを使用するためのサポートが追加されました。複数のデータベースを使用する場合は、 database.yml
で構成します。 ファイル。
development:
primary:
<<: *default
database: db/development.sqlite3
analytics:
adapter: sqlite3
database: db/analytics_dev.sqlite3
この構成は、2つのデータベース( primary
)を使用することをRailsに通知します。 およびanalytics
。前に見たように、移行は db / migrate
に保存されます デフォルトではディレクトリ。ただし、この場合、単一のディレクトリ内に両方のデータベースの移行を追加することはできません。 analytics
の移行を実行したくない primary
のデータベース データベースおよびその逆。複数のデータベースを使用している場合、weareは2番目のデータベースの移行を保存するためのパスを提供する必要があります。これは、 migrations_paths
を提供することで実行できます。 database.yml
内 。
development:
primary:
<<: *default
database: db/development.sqlite3
analytics:
adapter: sqlite3
database: db/analytics_dev.sqlite3
migrations_paths: db/analytics_migrate
次に、 analytics
の移行を作成できます 次のようなデータベース。
rails generate migration AddExperiments rule:string active:boolean --db=analytics
これにより、 db / analytics_migrate
内に移行が作成されます 、次のように実行できます。
rails db:migrate --db=analytics
rails db:migrate
のみを実行する場合 、すべてのデータベースの移行を実行します。
分析
データベースには独自のschema_migrations
があります 実行されている移行と実行されていない移行を追跡するためのテーブル。
展開中に移行を実行する
移行によってデータベースの状態が変わる可能性があり、コードはそれらの変更に依存する可能性があるため、新しいコードを適用する前に、最初に移行を実行することが非常に重要です。
Herokuベースのデプロイメントでは、移行は release
で実行できます。 Procfile
のフェーズ 。
# Profile
web: bin/puma -C config/puma.rb
release: bundle exec rake db:migrate
これにより、アプリdynoが再起動される前に移行が確実に実行されます。
Capistranoベースの展開では、サーバーを再起動する前に移行を実行する必要があります。
Dockerベースのデプロイでは、サイドカーコンテナーを実行して、アプリを再起動する前に最初に移行を実行できます。これは非常に重要です。そうしないと、新しいコードにデータベースの変更を適用する前に新しいコードの使用を開始すると、新しいコンテナが一貫性のない状態になる可能性があります。
結論
この投稿では、Railsでデータベース移行を作成する際のさまざまな側面について説明しました。また、移行の構成要素と、障害を処理し、必要に応じて移行をロールバックする方法についても説明しました。 Rails 6では複数のデータベースを使用でき、それぞれの移行を個別に追加する必要があります。最後に、新しいコードがデータベースの変更を使用し始める前にデータベースの変更が適切に適用されるように、展開中に移行を実行する方法を簡単に説明しました。
P.S。 Ruby Magicの投稿をマスコミから離れたらすぐに読みたい場合は、Ruby Magicニュースレターを購読して、投稿を1つも見逃さないでください。
-
React on Rails:シンプルなアプリの構築
アプリケーションのフロントエンド側全体を構築する企業は、多くの場合、バックエンドを構築するためにRailsなどの同じフレームワークを選択します。長年にわたり、これは最良かつ最も信頼できるオプションでした。 今日、絶えず進化するフロントエンドユニバースにある多数のライブラリとフレームワークにより、開発者はバックエンドとフロントエンドの両方に異なるプラットフォームを選択し、それらを簡単に統合できます。 Reactはフロントエンドのパンゲアの巨人になりました。 Ruby on Railsを使用している場合は、デフォルトのRailsページをReactコード(またはその他のフロントフレームワーク)に
-
Rails5でのAngularの使用
あなたは前にその話を聞いたことがあります。分散型で完全に機能するバックエンドAPIと、通常のツールセットで作成されたフロントエンドで実行されているアプリケーションがすでにあります。 次に、Angularに移動します。または、AngularをRailsプロジェクトと統合する方法を探しているだけかもしれません。これは、この方法を好むためです。私たちはあなたを責めません。 このようなアプローチを使用すると、両方の世界を活用して、たとえばRailsとAngularのどちらの機能を使用してフォーマットするかを決定できます。 構築するもの 心配する必要はありません。このチュートリアルは、この目的のた