20
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rails 4 :Detailed Association Reference

Last updated at Posted at 2014-07-11

#introduction
自習用のRails Guide「Detailed Association Reference」の日本語訳です。
http://guides.rubyonrails.org/association_basics.html#detailed-association-reference
based on version at 2014/07/11(JST)です。
めっちゃ間違ってるかもしれない。

#belongs_to Association Reference

##追加メソッド
belongs_toのアソシエーションでは以下のメソッドが使える。

  • association(force_reload = false)
  • association=(associate)
  • build_association(attributes = {})
  • create_association(attributes = {})
  • create_association!(attributes = {})

例えば以下のようなアソシエーションのモデルでは

class Order < ActiveRecord::Base
  belongs_to :customer
end

以下のメソッドが利用可能になる。

customer
customer=
build_customer
create_customer
create_customer!

###association(force_reload = false)

こんな感じ。該当するオブジェクトがない場合は、nilを返す。

@customer = @order.customer

###association=(associate)

オブジェクトに関連オブジェクトをアサインする。関連オブジェクトの外部キーにはオブジェクトの主キーが自動で代入される。

@order.customer = @customer

###build_association(attributes = {})
インスタンスは作られるけどsaveはされない。

@customer = @order.build_customer(customer_number: 123,
                                  customer_name: "John Doe")

###create_association(attributes = {})

インスタンスが作られて、かつsaveされる。

@customer = @order.create_customer(customer_number: 123,
                                   customer_name: "John Doe")

###create_association!(attributes = {})

上記のcreate_association と同じだけどレコードがinvalidのとき ActiveRecord::RecordInvalidをraiseする。

##オプション

アソシエーションを作成するとき、オプションが使える。こんな感じで。

class Order < ActiveRecord::Base
  belongs_to :customer, dependent: :destroy,
    counter_cache: true
end

belongs_to アソシエーションでは下記のオプションが使用可能。

:autosave
:class_name
:counter_cache
:dependent
:foreign_key
:inverse_of
:polymorphic
:touch
:validate

###:autosave
親オブジェクトをsaveしたとき、子オブジェクトを自動でsaveしたりdestroyしたりする。

:class_name

モデルの名前をアソシエーションから読み取れない場合、:class_nameでモデルの名前を指定できる。たとえばorderはcustomerに所属しているけど、customerを含むクラスの名前がPatronだったときとか。

class Order < ActiveRecord::Base
  belongs_to :customer, class_name: "Patron"
end

###:counter_cache

関連するオブジェクトの個数を把握するのに役立つ。たとえば下記のようなアソシエーションがある場合

class Order < ActiveRecord::Base
  belongs_to :customer
end
class Customer < ActiveRecord::Base
  has_many :orders
end

@customer.orders.size だと都度DBにCOUNT(*)クエリを発行するけど、これを避けるための方法が用意されている。

class Order < ActiveRecord::Base
  belongs_to :customer, counter_cache: true
end
class Customer < ActiveRecord::Base
  has_many :orders
end

上記でorderの個数にsizeメソッドでアクセスできるようになる。しかし:counter_cacheの利用には、belongs_toで指定された方のモデルに実際のカラムが必要になる。たとえば上記のケースでは、Customerorders_countという名称のカラムが必要になる。必要に応じて下記の記法で既存のカラムを利用することもできる。追加されたカラムはattr_readonlyでアクセス可能である。

class Order < ActiveRecord::Base
  belongs_to :customer, counter_cache: :count_of_orders
end
class Customer < ActiveRecord::Base
  has_many :orders
end

###:dependent
DBの制約を付与できる。

:destroy, 親オブジェクトがdestroyされたら、子オブジェクトもdestroyされる。
:delete, 親オブジェクトがdestroyされたら、destroyの呼び出しなしに即時に子オブジェクトのレコードをDBから削除する。

###:foreign_key

外部キーの名称を指定できる。

class Order < ActiveRecord::Base
  belongs_to :customer, class_name: "Patron",
                        foreign_key: "patron_id"
end

外部キー制約は付与されないので、外部キー制約を付与したいときは migrationに記述すること。

###:inverse_of

逆の順序のアソシエーションも利用可能にする。(よくわからない) :polymorphicオプションとは併用できない。

class Customer < ActiveRecord::Base
  has_many :orders, inverse_of: :customer
end
 
class Order < ActiveRecord::Base
  belongs_to :customer, inverse_of: :orders
end

###:polymorphic
ポリモーフィック関連を利用可能にする。

###:touch

:touch:true にすると、子オブジェクトがsaveされたりdestroyされたりしたとき、親オブジェクトの updated_atupdated_on にカレントタイムが設定される。下記の場合、orderをsaveしたりdestroyしたりすると、customerのタイムスタンプが更新される。

class Order < ActiveRecord::Base
  belongs_to :customer, touch: true
end
 
class Customer < ActiveRecord::Base
  has_many :orders
end

下記の記法でタイムスタンプの属性名称を指定可能。

class Order < ActiveRecord::Base
  belongs_to :customer, touch: :orders_updated_at
end

###validate
:validate:trueにすると子オブジェクト(ここではorder)がsaveされるとき検証をおこなう。

class Order < ActiveRecord::Base
  belongs_to :customer, validate: true
end
 
class Customer < ActiveRecord::Base
  has_many :orders
end

##スコープ

下記のスコープが使える。

class Order < ActiveRecord::Base
  belongs_to :customer, -> { where active: true },
                        dependent: :destroy
end

下記のスコープが使える。

where
includes
readonly
select

###where

親オブジェクトにたいする指定条件を付与する。下記の例ではorderactivetrueになっているcustomerにのみ所属するように指定される。

class Order < ActiveRecord::Base
  belongs_to :customer, -> { where active: true }
end

###includes

あいだにモデルをはさむアソシエーションで eager-loadしたいときにはincludeを利用できる。

class LineItem < ActiveRecord::Base
  belongs_to :order
end
 
class Order < ActiveRecord::Base
  belongs_to :customer
  has_many :line_items
end
 
class Customer < ActiveRecord::Base
  has_many :orders
end

もし頻繁に@line_item.order.customerするようなら、includeを使う方が効率的である。(DBへの接続回数を減らせるので。)

class LineItem < ActiveRecord::Base
  belongs_to :order, -> { includes :customer }
end
 
class Order < ActiveRecord::Base
  belongs_to :customer
  has_many :line_items
end
 
class Customer < ActiveRecord::Base
  has_many :orders
end

アソシエーションが直接の場合、includeは不要。上記の場合、@order.customerについては customerが自動で eager-loadされるのでorder-> { includes :customer }は不要。

###readonly
親オブジェクトに読み取り専用でアクセスするようになる。

###select
親オブジェクトのすべてのカラムをSELECTするようにSQLのSELECT文を上書きする。

##親オブジェクトの存在確認

.nil?で親がいるか確認できる。

if @order.customer.nil?
  @msg = "No customer found for this order"
end

##ObjectがSaveされるタイミング

saveされるまで自動でsaveはしない。

#has_one Association Reference
あとで書く。

#has_many Association Reference
あとで書く。

#has_and_belongs_to_many Association Reference
あとで書く。

#Association Callbacks
あとで書く。

#Association Extensions

匿名のモジュールをつかってメソッドなどを拡張できる。

class Customer < ActiveRecord::Base
  has_many :orders do
    def find_by_order_prefix(order_number)
      find_by(region_id: order_number[0..2])
    end
  end
end

複数のアソシエーションで利用したい場合、名前つきの拡張モジュールが定義できる。

module FindRecentExtension
  def find_recent
    where("created_at > ?", 5.days.ago)
  end
end
 
class Customer < ActiveRecord::Base
  has_many :orders, -> { extending FindRecentExtension }
end
 
class Supplier < ActiveRecord::Base
  has_many :deliveries, -> { extending FindRecentExtension }
end

下記の3つのproxy_association accessorでassociation proxyを参照できる。

proxy_association.owner
#=>returns the object that the association is a part of.
proxy_association.reflection
#=>returns the reflection object that describes the association.
proxy_association.target
#~>returns the associated object for belongs_to or has_one, or the collection of associated objects for has_many or has_and_belongs_to_many.
20
17
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?