ActiveRecordの関連のすべて
今回は ActiveRecord ついて説明していきたいと思います。 A Guide to Active Record Associationsと、 Active Record Query Interface を参考にしました。 また、図もこちらから拝借させていただきました。
Rails での関連
Rails では関連を設定することでモデルオブジェクトを接続し構造を作ることが出来ます。
関連の設定はモデルクラスに宣言的にメソッドを書き入れることによって行います。 使えるメソッドとしては大きく分けて以下の6種類があります。
- belongs_to
- has_one
- has_many
- has_many :through
- has_one :through
- has_and_belongs_to_many
ここからはこれらのメソッドをどのように使うのかを説明していきたいと思います。
一対一関連
一対一関連では belongs_to
と has_one
を利用します。英語をそのまま解釈すると belongs_to
は何かしらのオブジェクトに所有されるようなモデルに設定し、 has_one
は何かしらのオブジェクトを子に持つようなモデルに設定すべきということが分かります。
例えば、サプライヤーが一つだけ口座を持つ場合、サプライヤーが口座を持っていると考えて、Supplier が has_one
を使用し、 Account が belongs_to
を使うべきだと言うことが分かります。Account が Supplier に所有されていることを表すためには以下のように記述します。
class Account < ActiveRecord::Base
belongs_to :supplier
end
同様に、Supplier が Account を所有していることを表すためには以下のように記述します。
class Supplier < ActiveRecord::Base
has_one :account
end
テーブルの構造としては belongs_to 側の Account が Supplier の id を supplier_id として持つ必要があります。
多対一関連
多対一関連では belongs_to と has_many を利用します。
belongs_to
を使うのが所有される立場のオブジェクト、 has_many
を使うのが幾つかオブジェクトを持つ側になります。
例えば、顧客が注文をすることを考えると、顧客一人あたり幾つも注文する可能性があるため、顧客が複数の注文を所有していると考えることが出来ます。
このような場合、 Customer が Order を複数所有していることを表すためには以下のように記述します。
class Customer < ActiveRecord::Base
has_many :orders
end
次に Order が Customer に所有されていることを表すために以下のように記述します。
class Order < ActiveRecord::Base
belongs_to :customer
end
この時、テーブルの構造としては belongs_to を利用する Order が Customer の id を customer_id に格納する必要があります。
has_one :through
has_one :through
は別のオブジェクトを通して一つのオブジェクトを参照する場合に利用します。たとえば、サプライヤーは一つの口座を持っていて、口座は一つの口座履歴を持っているとすると、サプライヤーは口座を通して口座履歴を持っていると考えることが出来ます。このような場合に has_one :through
を利用します。
上記のような場合は以下のような形でモデルを記述します。
class Supplier < ActiveRecord::Base
has_one :account
has_one :account_history, :through => :account
end
class Account < ActiveRecord::Base
belongs_to :supplier
has_one :account_history
end
class AccountHistory < ActiveRecord::Base
belongs_to :account
end
このように設定することで、 @supplier.account_history
で口座履歴を取得することが出来るようになります。
has_many :through
has_many :through
は別のオブジェクトを通して複数のオブジェクトの参照をしたい場合に利用します。例えば、病院で患者が医師の予約を取りますが、患者は複数の予約をすることが出来ますし、一人の医者は複数の予約を受け付けることが出来ます。この時、医者は複数の予約を持ち(予約は医者に所有されている)、患者は複数の予約を持つ(予約は患者に所有されている)と考えることが出来ます。この時、医者が自分の担当する患者を調べるときに、予約を通して複数の患者を持つと考えることが出来ます。このような場合に、 has_many :through
を利用します。
上記のような状況では、以下のようにモデルを記述します。
class Physician < ActiveRecord::Base
has_many :appointments
has_many :patients, :through => :appointments
end
class Appointment < ActiveRecord::Base
belongs_to :physician
belongs_to :patient
end
class Patient < ActiveRecord::Base
has_many :appointments
has_many :physicians, :through => :appointments
end
上記のようにすることによって、 Physician は間接的に Patient を持つということを表現することが出来ます。たとえば、 @physician.patients
とすることで @physician
が対応する患者をすべて列挙することが出来ますし、 @patient.physicians
とすることで、 @patient
がお世話になる医者をすべて列挙することが出来ます。
多対多関連(その1)
多対多関連をRailsで表現する方法は二つありますが、ここでは has_and_belongs_to_many
を使った方法を説明します。
has_and_belongs_to は関連に何も情報がないときに利用します。もし、関連についての情報を持たせたい場合はもう一つの方法である、 has_many :through を利用した手法を使う必要があります。
例えば、組立機器は複数のパーツで構成されていますが、パーツは複数の機器にまたがって利用される可能性があるとします。このときのモデルの構造としては、組立機器が複数のパーツを持ち、パーツが複数の組立機器を持つということになります。これを実現するためには以下のように記述します。
class Assembly < ActiveRecord::Base
has_and_belongs_to_many :parts
end
class Part < ActiveRecord::Base
has_and_belongs_to_many :assemblies
end
これによって、 @assembly.parts
や
@part.assemblies
といった書き方が出来るようになります。
多対多関連(その2)
ここでは多対多関連を実現するためのもう一つの方法である、 has_many :through
を利用した方法を説明します。
実は、これは has_many :through
がその例になっています。というのも、医者は複数の患者を持っていると考えることが出来ますし、患者が複数の医者を持っていると見ることもできるからです。
つまり、多対多を実現したい二つのオブジェクトに対しての参照を持つ列に加えて、関連に関する情報を持つテーブルを作成すればよいことになります。
ActiveRecord のメソッド
この下に ActiveRecord で利用することが出来るメソッドの概要を紹介します。各メソッドの利用方法は別途調べてください。
- find: プライマリキーを利用してレコードを取得する
- first: 指定された条件で最初のレコードを取得する
- first!: first と同じ機能を持つが、レコードが見つからない場合は例外を出す
- last: 指定された条件で最後のレコードを取得する
- last!: last と同じ機能を持つが、レコードが見つからない場合は例外を出す
- all: すべてのレコードを取得する
- find_each: 各々のレコードに個別に処理をする
- find_in_batches: 各々のレコードを配列にまとめて処理をする
- where: 取得するレコードの条件を指定する
- order: 取得するレコードの順番を指定する
- select: データベースから取得してくる列を限定する
- limit: 取得するレコードの数を制限する
- offset: limit で取得するレコードをずらす
- group: グループを作る方法を指定する
- having: group by の列に対しての条件を指定する
- except: 指定メソッドで追加した条件を無効化する
- only: 指定したメソッド以外で追加した条件を無効化する
- reorder: デフォルトで指定されている並び替えの設定を上書きする
- reverse_order: 取得するレコードの順番を逆にする
- joins: テーブルの結合を行う
- includes: クエリの発行回数を抑制します
- lock: 排他制御を行う
- scope: よく使うクエリをメソッドとして定義する
- find_by_*: *の部分に指定した列名を条件に検索し最初の要素を返す
- find_all_by_*: *の部分に指定した列名を条件に検索しすべての要素を返す
- find_last_by_*: *の部分に指定した列名を条件に検索し最後の要素を返す
- find_by_*!: find_by_* と同じ機能を持つが、レコードが見つからない場合は例外を出す
- find_all_by_*!: find_all_by_* と同じ機能を持つが、レコードが見つからない場合は例外を出す
- find_last_by_*!: find_last_by_* と同じ機能を持つが、レコードが見つからない場合は例外を出す
- first_or_create: 最初の要素があればそれを返し、無ければ create が呼ばれる
- first_or_create!: 機能は first_or_create と同じだが、新しいレコードが不正ならば例外を出す
- first_or_create: first_or_create では create を呼ぶところで new を呼ぶ
- first_or_create!: first_or_create と同じ機能を持つが、新しいレコードが不正ならば例外を出す
- pluck: 一列の情報だけを取り出す
- exist?: レコードが存在するのかを確認する
- count: レコードの数を数える
- average: 指定した列の平均値を求める
- minimum: 指定した列の最小値を求める
- maximum: 指定した列の最大値を求める
- sum: 指定した列の値の和を求める