#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
で指定された方のモデルに実際のカラムが必要になる。たとえば上記のケースでは、Customer
にorders_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_at
や updated_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
親オブジェクトにたいする指定条件を付与する。下記の例ではorder
はactive
がtrue
になっている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.