読者です 読者をやめる 読者になる 読者になる

Rails Webook

自社のECを開発している会社で働いています。Rails情報やサービスを成長させる方法を書いていきます

Rails4で1対多のリレーションをモデルに実装する

Rails初級 Rails Model

Railsでは、ActiveRecordのhas_manybelogns_toを使うことで、DBの「1対多」のテーブル間のリレーションをモデルに簡単に実装することができます。

動作確認

  • Rails 4.1
  • ActiveRecord 4.1

目次

  1. 1対多関連とは
  2. 参照先の外部キーを追加する
  3. モデルにhas_manyとbelongs_toを追加する
  4. 使えるようになるメソッド


1. 1対多関連とは

説明のために次のER図を実装してみます。
f:id:nipe880324:20140810053124p:plain:w480

顧客1人が複数回注文をするので、「顧客」から見ると「注文」は"多"です。
また、1注文は必ず一人の顧客から注文されないので、「注文」から見ると「顧客」は"1"です。
このような関係を「1対多関係」といいます。




2. 参照先の外部キーを追加する

まず、多側のテーブル(今回はordersテーブル)にxxx_idという名で外部キーを追加します。
xxxの箇所は参照先のモデル名にする必要があります。
今回の場合は、Customerモデルを参照するため、customer_idにする必要があります。


Customerモデルとcustomersテーブルの作成をします。

rails g model Customer name:string
rake db:migrate


Orderモデルとordersテーブルの作成します。

rails g model Order customer_id:integer order_date:date
rake db:migrate

もし、モデルが既に存在していて外部キーのみを追加したい場合は、customer_idのみを追加するマイグレーションファイルを作成します。

rails g migration add_customer_id_to_orders customer_id:integer
rake db:migrate


3. モデルにhas_manyとbelongs_toを追加する

ではモデルファイルに1対多関連の宣言を追加します。

「1側」にhas_manyを追加します。
"Customer has_many orders"と読めば、「顧客(customer)は複数の注文(order)を持つ」と訳せます。
また、dependent: :destroyオプションを追加することで、customerレコードをdestoryメソッドで削除したら、Railsがそのcustomerに紐づいていたorderを全て削除してくれます。

# app/models/customer.rb 1側
class Customer < ActiveRecord::Base
  has_many :orders, dependent: :destroy
end


has_manyメソッドには次のようなオプションを指定できます。

  • class_nameオプションで関連するモデルのクラス名を指定でき、関連名と参照先のクラス名を異なるものにできできる。
  • foreign_keyオプションで参照先を参照する外部キーの名前を指定できる。デフォルトは、参照先のモデル名_id
  • dependentオプションで親オブジェクトが削除された時の扱いを指定できる。destroydelete_allなどが指定可能。
  • asオプションでポリモフィック関連を定義できる。
  • throughオプションでモデル接続の関連を設定できる。

など



「多側」にbelongs_toメソッドを記載します。
"Order belogns_to customer"と読めば、「注文(order)は1つの顧客(customer)に属する」と訳せます。

# app/models/order.rb 多側
class Order < ActiveRecord::Base
  belongs_to :customer
end


belogns_toメソッドには次のようなオプションを指定できます。

  • class_nameオプションで関連するモデルのクラス名を指定でき、関連名と参照先のクラス名を異なるものにできできる。
  • foreign_keyオプションで参照先を参照する外部キーの名前を指定できる。デフォルトは、参照先のモデル名_id
  • dependentオプションで親オブジェクトが削除された時の扱いを指定できる。destroydeleteが指定可能。
  • polymorphicオプションでポリモフィック関連を定義できる。

など

例えば、デフォルトのorder.customerではなく、order.userでアクセス可能にする

# app/models/order.rb 多側
class Order < ActiveRecord::Base
  belongs_to :user, class_name: "Customer", foreign_key: "customer_id"
end



4. 使えるようになるメソッド

これらを追加することで自動的に次のようなメソッドが使えるようになります。

# 作成
taro = Customer.create(name: "顧客1") # taroを作成し、DBに保存
order1 = taro.orders.build(order_date: Time.now) # order1を作成(newの代わりにbuildを使う)
order1.save # order1をDBに保存
order2 = taro.orders.create(order_date: Time.now) # order2を作成し、保存

# リレーション
taro.orders         # => orderオブジェクトの配列
taro.orders.exists? # => true (存在するか判定する)
taro.orders.empty?  # => false (空か判定する)
order1.customer     # => customerオブジェクト(orderを所持しているcustomer)

# taro.orders内のレコードのみから検索ができる
taro.orders.find(...)
taro.orders.find_by(...)
aro.orders.where(...)

# 削除
Order.count # => 2
taro.destory # => dependent: :destroyが指定されているので、orderも削除される
Order.count # => 0


以上です。

参考文献