Railsマイグレーションとモデルのアソシエーション

2021年5月8日プログラミング,Ruby on Rails

Ruby on Railsでモデルとマイグレーションファイルを作成して、環境に反映させる手順です。
一度本番に乗ってしまうとそうそう作成するものではないので、やり方を忘れた際の備忘録として使えるように書こうと思います。

実行環境
・Rails 7.1.3
・ruby 3.0.2p107

手順

・モデルファイルとマイグレーションファイルを同時に作る

・マイグレーションファイルに必要なメソッドを書く

・マイグレーションを実行する

・モデルファイルにアソシエーション定義を書く

その他、必要な情報があれば補足しながらやっていきます。

モデルファイルとマイグレーションファイルを同時に作る

以下のコマンドでモデルファイルとマイグレーションファイルを一括で作成できます。

基本的にテーブル同士のアソシエーションを持っていることが大半だと思うので、外部キーアリの書き方を記載しておきます。

ちなみに、外部キーが付くのは従属側のテーブルなので、間違って親テーブルに付けないよう注意が必要です。

rails g model モデル名 関連するテーブル名:references [カラム名:型] [オプション]

例)
rails g model Survey title:string description:string
rails g model Question survey:references question_title:string
 question_description:string
rails g model Option question:references content:string

changeメソッドを使うか判断する

作られたマイグレーションファイルを開くと、changeメソッドが既に定義されていると思います。

class CreateSurvey < ActiveRecord::Migration[7.1]
  def change
    create_table :surveys do |t|
      t.string :title
      t.string :description

      t.timestamps
    end
  end
end

Railsガイドによると、Changeメソッドでサポートされているマイグレーション定義に対してはロールバックができるようです。

上記のcreate_tableはchangeメソッドでサポートされているので、ロールバックしたい場合もchangeメソッドさえ定義されていれば問題ありません。

しかし、それ以外のマイグレーション定義を使用したい場合は、生成されたマイグレーションファイルにupメソッドとdownメソッドを定義してロールバックを可能としておく必要があります。

【余談】モデルファイルのみ、後から作りたい場合

しかし、この時点で既にマイグレーションファイルが存在していて、モデルファイルのみ後から生成したい場合はオプションに「-s(スキップ)」を設定します。

rails g model モデル名 [カラム名:型] -s

※既に存在するファイルのみ、生成をスキップ

【余談その2】マイグレーションファイルを二重で作成した場合

誤ってマイグレーションファイルを二重で作成してしまった場合は、以下のコマンドを打ってマイグレーションの状態を確かめましょう。


rails db:migrate:status

結果がdownとなっていれば、マイグレーションファイルを削除することができます。

マイグレーションを実行する

マイグレーションを実行する事でテーブルに対して変更が行われます。
コマンドは以下です。


rails db:migrate

モデルに対するアソシエーション定義

モデルファイルに対してテーブルのアソシエーションを行っていきます。

class Survey < ApplicationRecord
  has_many :questions
  accepts_nested_attributes_for :questions
end
class Question < ApplicationRecord
  belongs_to :survey
  has_many :options
  accepts_nested_attributes_for :options
end
class Option < ApplicationRecord
  belongs_to :question
end

Railsコンソールでアソシエーションの確認をする

アソシエーションが効いているか、Railsコンソールで確認してみます。

# 親レコードを作成
Survey.create(title: "abc", description: "def")
# 子レコードを作成
survey.questions.create(question_title: "child_title", question_description: "child_description")
# 孫レコードを作成
survey.questions[0].options.create(content: "grandchild")

今回は以上となります。