はじめに
ActiveRecord(モデル)は、MVCモデルのMに相当します。モデルはRailsアプリとデータベースの橋渡し役を担います。データベースの種類(MySQL、MariaDB、PostgreSQL、SQLite)にかかわらず、統一的なインターフェースでデータの操作(取得、作成、更新、削除)を行うことができます。
本記事では、ActiveRecord(モデル)の使い方についてまとめています。
モデルの基本
ORMフレームワーク
ORM (Object Relational Mapping) とは、データベースの種類を気にすることなくデータの操作(取得、作成、更新、削除)が行えること、またそのインターフェースのことをいいます。Railsはサードパーティ製のORMフレームワークを導入しなくても、ActiveRecord(モデル)がORMフレームワークの役割を担ってくれます。
モデルには以下の特徴があります。
- コントローラーまたはビューからモデルを介してデータベースにアクセスできる。
- 統一的なインターフェースでデータの操作(取得、作成、更新、削除)が行える。
- 関連付けられているモデル同士の相互操作が容易に行える。
- データ永続化の前にバリデーションチェックが行える。
モデルの作成
モデルを作成するには以下のコマンドを実行します。
$ rails generate model User name:string
モデル名は単数形(User
)を指定しますが、実際に作成されるテーブル名は複数形(users
)です。モデル名の後にカラム名と型をいくつでも指定することができます。
モデル作成コマンドを実行するとモデルの作成と同時にマイグレーションファイルも作成されます。作成されたマイグレーションファイルを基にマイグレーションを行うことで、データベースにテーブルが作成されます。マイグレーションについては以下の記事を参照してください。
モデルの削除
モデルを削除するには以下のコマンドを実行します。
$ rails destroy model User
モデル削除コマンドを実行してもデータベースのテーブルは削除されません。テーブルを残したままモデルを削除してしまうと不整合が起こるので、必ず該当のマイグレーションをロールバックなどして状態をdown
にしてからモデルを削除してください。
データの操作
データの操作(取得、作成、更新、削除)はコントローラーとビューのどちらからでも行うことができます。ただし、特段の理由がなければ(MVCモデルの理念に従い)コントローラーから行うようにしたほうがいいでしょう。
データの取得
データを取得するメソッドはたくさんあります。ここでは、よく使うであろうメソッドのみ紹介します。
find
find
は指定した主キーの値に一致するデータを取得します。主キーを条件にしているのでデータは必ず1件に絞り込まれます。
@user = User.find(10)
find_by
find_by
は指定したカラムの値に一致するデータを取得します。条件によっては1件に絞り込まれるとは限りませんが、条件に一致したデータのうち、最初の1件のみを取得します。条件はカンマ(,
)で区切って複数指定することができます。
@user = User.find_by(gender: 'male')
where
where
はfind_by
と似ていますが、条件に一致したデータをすべて取得する点が異なります。
@users = User.where(gender: 'male')
where
は条件を通常のSQLのように書くこともできます。値には引数を与えることもできます。
@users = User.where("gender = 'male' AND age > ?", params[:age])
# 以下はSQLインジェクションの危険があるため避ける
# @users = User.where("gender = 'male' AND age > #{params[:age]}")
order
order
は取得したデータを並べ替えます。ASC
(昇順:デフォルト)またはDESC
(降順)を指定します。
@users = User.all.order(name: :asc, updated_at: :desc)
find_each
find_each
は複数データをブロック(デフォルトで1000件)で取得し、ブロック単位で繰り返し処理を行います。最初のブロックの処理が完了したら次のブロックを取得し、また繰り返し処理を行います。最後のデータになるまでこれを繰り返します。
User.all.find_each do |user|
...
end
# 以下の書き方はメモリ消費が大きいので避ける
# User.all.each do |user|
# ...
# end
Enum
値がenum
型で設定されている場合、キーの名前を繋げるだけでデータを取得することができます。
# モデルに以下が定義されているとする
# enum gender: [male: 0, female: 1]
@users_male = User.male
@users_female = User.female
集計関数
一般的なSQLで用いられる集計関数(件数、平均、最小値、最大値、合計)を使うことができます。
@count = User.count
@average = User.average(:age)
@minimum = User.minimum(:age)
@maximum = User.maximum(:age)
@sum = User.sum(:age)
データの作成
データを作成するにはcreate
またはnew
とsave
を使います。create
の代わりにcreate!
を使うと、データの作成に失敗したときにActiveRecord::RecordInvalid
例外が発生します。
User.create(name: 'Jane', gender: 'female', age: 20)
User.create!(name: 'Jane', gender: 'female', age: 20)
@user = User.new(name: 'Jane', gender: 'female', age: 20)
@user.save
@user.save!
データの更新
データを更新するにはupdate
を使います。update
の代わりにupdate!
を使うと、データの更新に失敗したときにActiveRecord::RecordInvalid
例外が発生します。
@user = User.find(params[:id])
@user.update(age: 30)
@user.update!(age: 30)
# 以下の書き方でもOK
# @user = User.find(params[:id])
# @user.age = 30
# @user.save
複数データを一気に更新するにはupdate_all
を使います。
@users = User.find_by(gender: 'female')
@users.update_all(age: params[:new_age])
データの削除
データを削除するにはdestroy
またはdestroy_by
を使います。
@user = User.find(params[:id])
@user.destroy
User.destroy_by(id: params[:id])
複数データを一気に削除するにはdestroy_all
を使います。
User.destroy_all
モデルの関連付け
モデル同士が密接な関係にあるとき、モデルの関連付けを明示的に行うことでRailsによる様々な恩恵を受けることができます。例えば、特定のArticle
が複数のComment
を所持するとき(=複数のComment
は特定のArticle
に従属するとき)、Article
が削除されたらComment
も削除されなければなりません。モデルの関連付けを行っておくとComment
の削除はRailsが自動で行ってくれます。
関連付けの種類
Railsで行える関連付けの種類は以下の通りです。
関連付け | 用途 | 意味 |
---|---|---|
belongs_to |
1対1 | 特定のモデルが特定のモデルに従属する。 |
has_one |
1対1 | 特定のモデルが特定のモデルを所持する。 |
has_many |
1対多 | 特定のモデルが複数のモデルを所持する。 |
has_many :through |
多対多 | 複数のモデルが複数のモデルを所持する。 |
has_one :through |
1対1 | 特定のモデルが特定のモデルを所持する。 |
has_and_belongs_to_many |
多対多 | 複数のモデルが複数のモデルを所持する。 |
1対1の関連付け
1対1の関連付けを行うには、一方のモデルにbelongs_to
を設定し、もう一方のモデルにhas_one
を設定します。belongs_to
もhas_one
も対象は特定のモデルなので、指定するモデル名は単数形にします。
belongs_to
とhas_one
をどちらのモデルに設定するかは、モデルの意味を考える必要があります。以下の例だと、「給料は従業員を所持する」ではなく「従業員は給料を所持する」と考えたほうが自然なので、Employee
にhas_one
を設定し、Salary
にbelongs_to
を設定します。
# 従業員モデル
has_one :salary, dependent: destroy
# 給料モデル
belongs_to :employee
belongs_to
を設定するテーブルには外部キーカラムを追加する必要があります。以下はsalaries
テーブルにemployee
カラムを追加し、外部キーを設定している例です。
# テーブル作成のマイグレーション
t.references :employee, null: false, foreign_key: true
# カラム追加のマイグレーション
add_reference :salaries, :employee, foreign_key: true
モデルに外部キーを設定する方法について詳しくは以下の記事を参照してください。
1対多の関連付け
1対多の関連付けを行うには、一方のモデルにbelongs_to
を設定し、もう一方のモデルにhas_many
を設定します。has_many
の対象は複数のモデルなので、指定するモデル名は複数形にします。
# 記事モデル
has_many :comments, dependent: destroy
# コメントモデル
belongs_to :article
多対多の関連付け
多対多の関連付けを行うには、一方のモデルにhas_many :through
を設定し、もう一方のモデルにもhas_may :through
を設定します。そして、それぞれの中間となるモデルにbelongs_to
を設定します。
# 記事モデル
has_many :tagmaps, dependent: destroy
has_many :tags, through: tagmaps
# タグマップモデル
belongs_to :article
belongs_to :tag
# タグモデル
has_many :tagmaps, dependent: destroy
has_many :articles, through: tagmaps
多対多の関連付けはhas_and_belongs_to_many
を使う方法もあります。
# 記事モデル
has_and_belongs_to_many :tags
# タグモデル
has_and_belongs_to_many :articles
has_and_belongs_to_many
関連付けを使った場合、中間となる結合テーブルを作成する必要があります。このテーブルに主キーを設定すると関連付けが正常に動かないことがあるため、必ずid: false
を設定する必要があります。
class CreateArticlesTagsJoinTable < ActiveRecord::Migration[6.0]
def change
create_table :articles_tags, id: false do |t|
t.bigint :article_id
t.bigint :tag_id
end
add_index :articles_tags, :article_id
add_index :articles_tags, :tag_id
end
end
create_join_table
メソッドを使うこともできます。
class CreateArticlesTagsJoinTable < ActiveRecord::Migration[6.0]
def change
create_join_table :articles, :tags do |t|
t.index :article_id
t.index :tag_id
end
end
end
関連付けモデルのデータ操作
モデルに適切な関連付けを設定していると、一方のモデルのインスタンスから関連付けられているもう一方のモデルのデータを操作することができます。
1対1のデータ操作
1対1の関連付けでは、一方のモデルからもう一方のモデルに対して、同じようなメソッドを使って相互にデータ操作することができます。以下はEmployee
モデルとSalary
モデルの例です。両者には従属関係(has_one
とbelongs_to
)が存在しますが、それを意識することなくデータ操作することができます。とはいえ、従属元モデル(belongs_to
を設定したモデル)から従属先モデル(has_one
を設定したモデル)のデータ操作を行うことはほぼありません。
モデル同士は1対1のため、関連するモデルのデータ操作をするときはモデル名を単数形にします。また、関連するモデルのデータを作成するときは、build_association
やcreate_association
のように、末尾に関連するモデル名を付与したメソッドを使います。
### データの取得 ###
# Employeeに関連するSalaryを取得
@salary = @employee.salary
# Salaryに関連するEmployeeを取得
@employee = @salary.employee
### データの作成 ###
# Employeeに関連するSalaryを作成
@salary = @employee.build_salary(salary_params)
@salary.save
# or
@employee.create_salary(salary_params)
@employee.create_salary!(salary_params)
# Salaryに関連するEmployeeを作成
@employee = @salary.build_employee(employee_params)
@employee.save
# or
@salary.create_employee(employee_params)
@salary.create_employee!(employee_params)
### データの更新 ###
# Employeeに関連するSalaryを更新
@salary = @employee.salary
@salary.reward = params[:reward]
@salary.update
# Salaryに関連するEmployeeを更新
@employee = @salary.employee
@employee.class = params[:class]
@employee.update
1対多のデータ操作
1対多の関連付けでは、複数のモデルを所持するモデル(has_many
を設定したモデル)と特定のモデルに従属するモデル(belongs_to
を設定したモデル)とでデータ操作の方法が異なるため、1対多のデータ操作ではモデル同士の従属関係を意識する必要があります。とはいえ、従属元モデル(belongs_to
を設定したモデル)から従属先モデル(has_one
を設定したモデル)のデータ操作を行うことはほぼありません。
モデル同士は1対多のため、従属先モデル(has_many
を設定したモデル)から従属元モデル(belongs_to
を設定したモデル)のデータ操作をするときはモデル名を複数形にします。
### データの取得 ###
# Articleに関連するすべてのCommentを取得
@comments = @article.comments
# Commentに関連するArticleを取得
@article = @comment.article
### データの作成 ###
# Articleに関連するCommentを作成
@comment = @article.comments.build(comment_params)
@comment.save
# or
@article.comments.create(comment_params)
@article.comments.create!(comment_params)
# Commentに関連するArticleを作成
@article = @comment.build_article(article_params)
@article.save
# or
@comment.create_article(article_params)
@comment.create_article!(article_params)
### データの更新 ###
# Aarticleに関連するCommentを更新
@comment = @article.comments.find(10)
@comment.enable = false
@comment.update
# Commentに関連するArticleを更新
@article = @comment.article
@article.title = params[:title]
@article.update
多対多のデータ操作
多対多の関連付けでは、相互に複数のモデルを所持するモデル(has_many
を設定したモデル)のデータ操作を行います。多対多の関連付けでは相互にデータ操作することがあります。
モデル同士は多対多のため、相互のデータ操作をするときはモデル名を複数形にします。
### データの取得 ###
# Articleに関連するすべてのTagを取得
@tags = @article.tags
# Tagに関連するすべてのArticleを取得
@articles = @tag.articles
### データの作成 ###
# Articleに関連するTagを作成
@tag = @article.tags.build(tag_params)
@tag.save
# or
@article.tags.create(tag_params)
@article.tags.create!(tag_params)
# Tagに関連するArticleを作成
@article = @tag.articles.build(article_params)
@article.save
# or
@tag.articles.create(article_params)
@tag.articles.create!(article_params)
### データの更新 ###
# Aarticleに関連するTagを更新
@tag = @article.tags.where(name: 'Rails')
@tag.name = 'Ruby on Rails'
@tag.update
# Tagに関連するArticleを更新
@article = @tag.articles.find(20)
@article.author = params[:author]
@article.update
テーブルやカラムにコメントをつける
Rails 5からテーブルやカラムにコメントをつけることができるようになりました。つけたコメントはdb/schema.rb
で確認できるので、別途テーブル設計書などのドキュメントを参照しなくてもよくなりました。
テーブル作成時
テーブル作成時にコメントをつけるには、マイグレーションファイルを以下のように修正します。
YYYYMMDDHHMMSS_create_articles.rb
class CreateArticlets < ActiveRecord::Migration
def change
create_table :articles, comment: '記事' do |t|
t.string :title, comment: 'タイトル'
t.text :content, comment: '本文'
t.timestamps
end
end
end
テーブル作成後
テーブル作成後にコメントを追加するには、以下のようなマイグレーションファイルを作成します。
YYYYMMDDHHMMSS_add_comments_to_articles.rb
class AddCommmentsToStaffEmail < ActiveRecord::Migration
def change
change_table_comment :articles, '記事'
change_column_comment :articles, :title, 'タイトル'
change_column_comment :articles, :content, '本文'
end
end
カラムの追加と同時にコメントを追加するには、マイグレーションファイルを以下のように修正します。
YYYYMMDDHHMMSS_add_category_to_articles.rb
class AddCategoryToArticles < ActiveRecord::Migration
def change
add_column :articles, :category, :string, comment: 'カテゴリー'
end
end
まとめ
Webアプリケーションにデータベースはほとんど必須と言っても過言ではありません。実際、世の中に存在するほとんどのWebアプリケーションはデータベースを持ち独自のデータを保持しています。WebアプリケーションにとってなくてはならないデータベースとRailsアプリを繋ぐActiveRecord(ビュー)の使い方を把握しておく必要があります。
本記事を参考にして、ActiveRecord(ビュー)の使い方を覚えていただければと思います。