Last-modified: 2010-09-20 (月) 12:05:19 (2d)
Active Record Associations †
原文 †http://guides.rubyonrails.org/association_basics.html Active Record アソシエーションのガイド(A Guide to Active Record Associations) †このガイドでは、 Active Record アソシエーション機能を説明します。
1 何故アソシエーションなの?(Why Associations?) †何故モデル間にアソシエーションが必要なのでしょう? class Customer < ActiveRecord::Base end class Order < ActiveRecord::Base end 今、我々は既存の顧客のために新しい注文を追加したいと仮定します。 @order = Order.create(:order_date => Time.now, :customer_id => @customer.id) また顧客の削除を考えましょう、その注文の全てを同様に削除することを確保します。 @orders = Order.find_by_customer_id(@customer.id) @orders.each do |order| order.destroy end @customer.destroy Active Record のアソシエーションで、これらを合理化するほかに、 class Customer < ActiveRecord::Base has_many :orders, :dependent => :destroy end class Order < ActiveRecord::Base belongs_to :customer end この変更により、特定の顧客のための新しい注文を作成することが簡単になります: @order = @customer.orders.create(:order_date => Time.now) 顧客とその注文の全てを削除するのはもっと簡単です: @customer.destroy アソシエーションの他の種類の詳細を学ぶために、このガイドの次のセクションを読んで下さい。 2 アソシエーションの種類(The Types of Associations) †Rails では、アソシエーションは二つの Active Record モデルの間の繋がりです。
このガイドの残りの部分では、宣言方法と、アソシエーションの様々な形式の使用方法を学びます。 2.1 belongs_to アソシエーション(The belongs_to Association) †belongs_to アソシエーションは、宣言したモデルの全てのインスタンスがもう一方のモデルのインスタンスの一つに "belongs_to (属する)" ように、 class Order < ActiveRecord::Base belongs_to :customer end 2.2 has_one アソシエーション(The has_one Association) †has_one アソシエーションもまた、もう一方のモデルとの1対1の繋がりを設定します、が、少しだけ意味(と結果)が違います。 class Supplier < ActiveRecord::Base has_one :account end 2.3 has_many アソシエーション(The has_many Association) †has_many アソシエーションは、もう一方のモデルとの1対多の繋がりを示します。 class Customer < ActiveRecord::Base has_many :orders end もう一方のモデルの名前は、has_many 関連の宣言時に複数形にされます。 2.4 has_many :through アソシエーション(The has_many :through Association) †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 結合モデルのコレクションは、APIを介して管理することが出来ます。 physician.patients = patients
と割り当てたなら、新しく関連付けられたオブジェクトのために新しい結合モデルが作られ、 結合モデルの自動的な削除機能は直接的で、 destroy コールバックはトリガされません。 has_many :through アソシエーションは、ネストした has_many アソシエーションを介した "ショートカット" を設定するのにも便利です。 class Document < ActiveRecord::Base has_many :sections has_many :paragraphs, :through => :sections end class Section < ActiveRecord::Base belongs_to :document has_many :paragraphs end class Paragraph < ActiveRecord::Base belongs_to :section end 2.5 has_one :through アソシエーション(The has_one :through Association) †has_one :through アソシエーションは、もう一方のモデルとの1対1の繋がりを設定します。 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 2.6 has_and_belongs_to_many アソシエーション(The has_and_belongs_to_many Association) †has_and_belongs_to_many アソシエーションは、仲介のモデルを挟まず、もう一方のモデルとの直接の多対多の繋がりを作ります。 class Assembly < ActiveRecord::Base has_and_belongs_to_many :parts end class Part < ActiveRecord::Base has_and_belongs_to_many :assemblies end 2.7 belongs_to と has_one の選択(Choosing Between belongs_to and has_one) †二つのモデル間の1対1の関係を設定したければ、 区別は、どちらに外部キーを配置するかです。(外部キーはbelongs_to アソシエーションを宣言したクラスのテーブルに配置されます。) class Supplier < ActiveRecord::Base has_one :account end class Account < ActiveRecord::Base belongs_to :supplier end 対応するマイグレーションはこのようになるでしょう。 class CreateSuppliers < ActiveRecord::Migration def self.up create_table :suppliers do |t| t.string :name t.timestamps end create_table :accounts do |t| t.integer :supplier_id t.string :account_number t.timestamps end end def self.down drop_table :accounts drop_table :suppliers end end t.integer :supplier_id を使うことで外部キーの名前は明らかで明示的なものに出来ます。 2.8 has_many :through と has_and_belongs_to_many の選択(Choosing Between has_many :through and has_and_belongs_to_many) †Rails は、2つの異なる方法をモデル間の多対多の関係を宣言するために提供しています。 class Assembly < ActiveRecord::Base has_and_belongs_to_many :parts end class Part < ActiveRecord::Base has_and_belongs_to_many :assemblies end 多対多を宣言する二番目の方法は、 has_many :through を使うことです。 class Assembly < ActiveRecord::Base has_many :manifests has_many :parts, :through => :manifests end class Manifest < ActiveRecord::Base belongs_to :assembly belongs_to :part end class Part < ActiveRecord::Base has_many :manifests has_many :assemblies, :through => :manifests end 最もシンプルな経験則は、独立した実体として関連モデルを動作させる必要がある場合 バリデーション、コールバック、結合モデル上の追加の要素、が必要であれば、 has_many :through を使うべきでしょう。 2.9 polymorphic アソシエーション(Polymorphic Associations) †アソシエーション上のちょっと高度な応用は、 polymorphic アソシエーションです。 class Picture < ActiveRecord::Base belongs_to :imageable, :polymorphic => true end class Employee < ActiveRecord::Base has_many :pictures, :as => :imageable end class Product < ActiveRecord::Base has_many :pictures, :as => :imageable end ポリモーフィックな belongs_to 宣言を、他のモデルが利用出来るインターフェイスの設定として考えることができます。 同様に、 @product.pictures で取得することもできます。 Picture モデルのインスタンスがあるなら、 @picture.imageable を通じてその親を取得することもできます。 class CreatePictures < ActiveRecord::Migration def self.up create_table :pictures do |t| t.string :name t.integer :imageable_id t.string :imageable_type t.timestamps end end def self.down drop_table :pictures end end このマイグレーションは t.references 形式を使うことにより、簡素化できます。 class CreatePictures < ActiveRecord::Migration def self.up create_table :pictures do |t| t.string :name t.references :imageable, :polymorphic => true t.timestamps end end def self.down drop_table :pictures end end 2.10 自己結合(Self Joins) †データモデルの設計をしていると、時々、自己関連する必要があるモデルを見つけるでしょう。 class Employee < ActiveRecord::Base has_many :subordinates, :class_name => "Employee", :foreign_key => "manager_id" belongs_to :manager, :class_name => "Employee" end この設定により、 @employee.subordinates と @employee.manager が検索可能になります。 3 Tips、Tricks、警告(Tips, Tricks, and Warnings) †Rails アプリケーション内で Active Record アソシエーションを効率的に利用するために知っておくべきいくつかの事柄があります:
3.1 キャッシュのコントロール(Controlling Caching) †アソシエーションのメソッド全ては、キャッシュを中心として構築されていて、 customer.orders # データベースから orders を取得 customer.orders.size # orders のキャッシュコピーを使う customer.orders.empty? # orders のキャッシュコピーを使う データはアプリケーションの他の部分で変更されているかもしれないので、もしキャッシュを再読み込みしたいなら? customer.orders # データベースからordersを取得 customer.orders.size # ordersのキャッシュコピーを使う customer.orders(true).empty? # ordersのキャッシュコピーを捨てる # 再度データベースから取得する 3.2 名前の衝突を回避する(Avoiding Name Collisions) †アソシエーションのために好きな名前を使っていいわけではありません。 3.3 スキーマのアップデート(Updating the Schema) †アソシエーションは非常に便利ですが、魔法ではありません。 3.3.1 belongs_to アソシエーションのための外部キーの作成 belongs_to アソシエーションを宣言するとき、適切な外部キーを作成する必要があります。 class Order < ActiveRecord::Base belongs_to :customer end この宣言は、 orders テーブル上の適切な外部キー宣言によってバックアップされている必要があります: class CreateOrders < ActiveRecord::Migration def self.up create_table :orders do |t| t.datetime :order_date t.string :order_number t.integer :customer_id end end def self.down drop_table :orders end end 基礎となるモデルを作った後に、アソシエーションを作りたいなら、 3.3.2 has_and_belongs_to_many アソシエーションのための結合テーブルの作成 has_and_belongs_to_many アソシエーションを作るなら、結合テーブルを明示的に作る必要があります。 モデル名間の優先順位は、 String の < 演算子を使用して計算されます。 どのような名前かに関わらず、適切なマイグレーションで結合テーブルを手動で生成する必要があります。 class Assembly < ActiveRecord::Base has_and_belongs_to_many :parts end class Part < ActiveRecord::Base has_and_belongs_to_many :assemblies end これらは、マイグレーションで assemblies_parts テーブルを作成し、バックアップする必要があります。 class CreateAssemblyPartJoinTable < ActiveRecord::Migration def self.up create_table :assemblies_parts, :id => false do |t| t.integer :assembly_id t.integer :part_id end end def self.down drop_table :assemblies_parts end end テーブルはモデルを表していないので、 create_table に :id => false を渡します。 3.4 アソシエーションのスコープのコントロール(Controlling Association Scope) †デフォルトでは、アソシエーションは現在のモジュールのスコープ内でだけ、オブジェクトを探します。 module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account end class Account < ActiveRecord::Base belongs_to :supplier end end end これは、正常に動作します。なぜなら、 Supplier と Account クラスはどちらも同じスコープで定義されているからです。 module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account end end module Billing class Account < ActiveRecord::Base belongs_to :supplier end end end あるモデルと別の名前空間のモデルを関連付けるために、アソシエーションの宣言で完全なクラス名を指定する必要があります: module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account, :class_name => "MyApplication::Billing::Account" end end module Billing class Account < ActiveRecord::Base belongs_to :supplier, :class_name => "MyApplication::Business::Supplier" end end end 4 アソシエーションのリファレンスの詳細(Detailed Association Reference) †次のセクションでは、アソシエーションの各種の詳細を見ていきます。 4.1 belongs_to アソシエーションのリファレンス(belongs_to Association Reference) †belongs_to アソシエーションは他のモデルとの1対1の繋がりを作ります。 4.1.1 belongs_to により追加されたメソッド belongs_to アソシエーションを宣言するとき、宣言クラスは、自動的にそのアソシエーションに関する4つのメソッドを得ます。
これら全てのメソッドで、 association は belongs_to に第一引数として渡されるシンボルと置き換えられます。 class Order < ActiveRecord::Base belongs_to :customer end Order モデルの全てのインスタンスは、これらのメソッドを持ちます: customer customer= build_customer create_customer 4.1.1.1 association(force_reload = false) association メソッドは、もしあれば関連するオブジェクトを返します。 @customer = @order.customer 関連先のオブジェクトが既にこのオブジェクトのためにデータベースから取得されている場合、キャッシュされたバージョンが返されるでしょう。 4.1.1.2 association=(associate) association= メソッドは、関連先のオブジェクトを、このオブジェクトに割り当てます。 @order.customer = @customer 4.1.1.3 build_association(attributes = {}) build_association メソッドは、関連先の型の新しいオブジェクトを返します。 @customer = @order.build_customer(:customer_number => 123, :customer_name => "John Doe") 4.1.1.4 create_association(attributes = {}) create_association メソッドは、関連先の型の新しいオブジェクトを返します。 @customer = @order.create_customer(:customer_number => 123, :customer_name => "John Doe") 4.1.2 belongs_to のオプション 多くの状況で、カスタマイズすること無しに belongs_to のデフォルトの動作を使用できます。 class Order < ActiveRecord::Base belongs_to :customer, :counter_cache => true, :conditions => "active = 1" end belongs_to アソシエーションはこれらのオプションをサポートしています:
4.1.2.1 :autosave :autosave オプションが true に設定されているなら、 4.1.2.2 :class_name もう一方のモデルの名前が、アソシエーションの名前から得られない場合、 class Order < ActiveRecord::Base belongs_to :customer, :class_name => "Patron" end 4.1.2.3 :conditions :conditions オプションは、関連先のオブジェクトが満たす必要がある制約を指定するのに使います。 class Order < ActiveRecord::Base belongs_to :customer, :conditions => "active = 1" end 4.1.2.4 :counter_cache :counter_cache オプションはより効率的にオブジェクトに属している数を得るために使用することができます。 class Order < ActiveRecord::Base belongs_to :customer end class Customer < ActiveRecord::Base has_many :orders end これらの宣言では、 @customer.orders.size の値を参照するのに、 COUNT(*) クエリを実行するためにデータベースを呼び出す必要があります。 class Order < ActiveRecord::Base belongs_to :customer, :counter_cache => true end class Customer < ActiveRecord::Base has_many :orders end この宣言では、 Rails は最新のキャッシュの値を保持し、次に size メソッドに応答した値を返すでしょう。 :counter_cache オプションは、belongs_to 宣言を含むモデル上で指定されますが、実際のカラムは、関連先のモデルに追加する必要があります。 class Order < ActiveRecord::Base belongs_to :customer, :counter_cache => :count_of_orders end class Customer < ActiveRecord::Base has_many :orders end カウンターキャッシュカラムが、 attr_readonly を通じて、格納されているモデルの読み込み専用の属性のリストに追加されます。 4.1.2.5 :dependent :depend オプションに :destroy を設定したなら、このオブジェクトを削除すると、 もう一方のクラスとhas_many アソシエーションで繋がっている、belongs_to アソシエーションにこのオプションを指定しないほうがよいでしょう。 4.1.2.6 :foreign_key 規約により Rails は、このモデルの外部キーを保持するために使われるカラム名は、関連先のモデル名のお尻に_idを付けたものだと推測します。 class Order < ActiveRecord::Base belongs_to :customer, :class_name => "Patron", :foreign_key => "patron_id" end いずれにせよ、Railsは外部キーカラムを作成しないでしょう。 4.1.2.7 :include このアソシエーションが使われた時に Eager Loading されるべき二次アソシエーションを指定するために、 :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_items )から直接顧客を取り出すなら、(@line_item.order.customer) class LineItem < ActiveRecord::Base belongs_to :order, :include => :customer end class Order < ActiveRecord::Base belongs_to :customer has_many :line_items end class Customer < ActiveRecord::Base has_many :orders end :include を即時的なアソシエーションに使う必要はありません。 4.1.2.8 :polymorphic :polymorphic オプションに true を渡すと、これが polymorphic アソシエーションだということを示します。 4.1.2.9 :readonly :readonly オプションを true に設定すると、関連先のオブジェクトをそのアソシエーション経由で取得するとき、読み込み専用になるでしょう。 4.1.2.10 :select :select オプションは、関連先オブジェクトについてのデータを取得するために使われる SQL SELECT 句をオーバーライドすることになるでしょう。 belongs_to アソシエーション上で :select オプションを設定するなら、 4.1.2.11 :touch :touch オプションを :true に設定したなら、関連先オブジェクト上の updated_at か updated_on タイムスタンプは、 class Order < ActiveRecord::Base belongs_to :customer, :touch => true end class Customer < ActiveRecord::Base has_many :orders end この場合、 order が保存か破壊されることは、関連先の customer 上のタイムスタンプを更新するでしょう。 class Order < ActiveRecord::Base belongs_to :customer, :touch => :orders_updated_at end 4.1.2.12 :validate :validate オプションに true を設定するなら、このオブジェクトを保存するたびに、関連先のオブジェクトはバリデーションされるでしょう。 4.1.3 関連先オブジェクトがあるかどうか、どうやって知るの? 関連先オブジェクトがあるかどうか知るには、 association.nil? をチェックするだけです。 if @order.customer.nil? @msg = "No customer found for this order" end 4.1.4 オブジェクトが保存されるのはいつ? belongs_to アソシエーションにオブジェクトを割り当てても、自動的にオブジェクトを保存しません。 4.2 has_one アソシエーションのリファレンス(has_one Association Reference) †has_one アソシエーションは、他のモデルとの一対一の繋がりを作ります。 4.2.1 has_one によって追加されるメソッド has_one アソシエーションを宣言する時、宣言するクラスは自動的にそのアソシエーションに関係がある4つのメソッドを手に入れます:
これらのメソッド全てで、 association は has_one に第一引数として渡されるシンボルと置き換えられます。 class Supplier < ActiveRecord::Base has_one :account end Supplier モデルの全てのインスタンスはこれらのメソッドを持っています。 account account= build_account create_account 4.2.1.1 association(force_reload = false) association メソッドは、もしあれば関連するオブジェクトを返します。 @account = @supplier.account 関連先のオブジェクトが既にこのオブジェクトのデータベースから取得されている場合、キャッシュされたバージョンが返されるでしょう。 4.2.1.2 association=(associate) association= メソッドは、関連先のオブジェクトを、このオブジェクトに割り当てます。 @supplier.account = @account 4.2.1.3 build_association(attributes = {}) build_association メソッドは、関連先の型の新しいオブジェクトを返します。 @account = @supplier.build_account(:terms => "Net 30") 4.2.1.4 create_association(attributes = {}) create_association メソッドは、関連先の型の新しいオブジェクトを返します。 @account = @supplier.create_account(:terms => "Net 30") 4.2.2 has_one のオプション 多くの状況で、カスタマイズすること無しに has_one のデフォルトの動作を使用できます。 class Supplier < ActiveRecord::Base has_one :account, :class_name => "Billing", :dependent => :nullify end has_one アソシエーションはこれらのオプションをサポートしています。
4.2.2.1 :as :as オプションの設定は、 polymorphic アソシエーションだということを示します。 4.2.2.2 :autosave :autosave オプションが true に設定されているなら、 4.2.2.3 :class_name もう一方のモデルの名前が、アソシエーションの名前から得られない場合、 :class_name オプションを、モデルの名前を供給するために使えるでしょう。 class Supplier < ActiveRecord::Base has_one :account, :class_name => "Billing" end 4.2.2.4 :conditions :conditions オプションは関連先のオブジェクトが満たす必要がある制約を指定するのに使います。 class Supplier < ActiveRecord::Base has_one :account, :conditions => "confirmed = 1" end 4.2.2.5 :dependent :depend オプションに :destroy を設定すると、このオブジェクトを削除すると関連先のオブジェクトを削除するために 4.2.2.6 :foreign_key 規約により、 Rails はもう一方のモデルの外部キーを保持するために使われるカラム名は、このモデル名のお尻に_idを付けたものだと推測します。 class Order < ActiveRecord::Base class Supplier < ActiveRecord::Base has_one :account, :foreign_key => "supp_id" end end いずれにせよ、 Rails は外部キーカラムを作成しません。 4.2.2.7 :include このアソシエーションが使われた時に Eager Loading されるべき二次アソシエーションを指定するために :include オプションを使うことが出来ます。 class Supplier < ActiveRecord::Base has_one :account end class Account < ActiveRecord::Base belongs_to :supplier belongs_to :representative end class Representative < ActiveRecord::Base has_many :accounts end 頻繁に業者( supplier )から直接、代表者( representative )を取り出すなら、(@supplier.account.representative) class Supplier < ActiveRecord::Base has_one :account, :include => :representative end class Account < ActiveRecord::Base belongs_to :supplier belongs_to :representative end class Representative < ActiveRecord::Base has_many :accounts end 4.2.2.8 :order :order オプションは関連先オブジェクトが受け取られる順番を指示します。(SQL の ORDER BY 句の構文が使われます。) 4.2.2.9 :primary_key 規約により Rails は、カラムがこのモデルの主キーを保持するのに id を使っていると推測します。 4.2.2.10 :readonly :readonly オプションを true に設定すると、関連先のオブジェクトをこのアソシエーション経由で取得するとき読み込み専用になるでしょう。 4.2.2.11 :select :select オプションは、関連先オブジェクトについてのデータを取得するために使われる SQL SELECT 句をオーバーライドすることになるでしょう。 4.2.2.12 :source :source オプションは has_one :through アソシエーションための元になるアソシエーション名を指定します。 4.2.2.13 :source_type :source_type オプションは polymorphic アソシエーションを通じて、 has_one :through アソシエーションの元になる型を指定します。 4.2.2.14 :through :through オプションは、それを通じてクエリを実行する結合モデルを指定します。 4.2.2.15 :validate :validate オプションに true を設定するなら、このオブジェクトを保存するたびに、関連先のオブジェクトはバリデーションされるでしょう。 4.2.3 関連先オブジェクトがあるかどうか、どうやって知るの? 関連先オブジェクトがあるかどうか知るには、 association.nil? をチェックするだけです。 if @supplier.account.nil? @msg = "No account found for this supplier" end 4.2.4 オブジェクトが保存されるのはいつ? has_one アソシエーションにオブジェクトを割り当てている場合、オブジェクトは自動的に保存されます。(その外部キーを更新するために) これらのいずれかの保存がバリデーションエラーで失敗した場合、代入文は false を返し、代入自体はキャンセルされます。 親オブジェクト( has_one アソシエーションを宣言しているほう)が保存されなければ( new_record? が true を返す)、子オブジェクトは保存されません。 オブジェクトを保存せずに has_one アソシエーションにオブジェクトを割り当てたい場合、association.buildメソッドを使いましょう。 4.3 has_many アソシエーションのリファレンス(has_many Association Reference) †has_many アソシエーションは他のモデルとの一対多の繋がりを作ります。 4.3.1 追加されるメソッド has_many アソシエーションを宣言する時、宣言するクラスは自動的にそのアソシエーションに関係がある13のメソッドを手に入れます:
これらのメソッド全てで、 collection は has_many に第一引数として渡されるシンボルと置き換えられます。 class Customer < ActiveRecord::Base has_many :orders end Customer モデルの全てのインスタンスはこれらのメソッドを持っています。 orders(force_reload = false) orders<<(object, ...) orders.delete(object, ...) orders=objects order_ids order_ids=ids orders.clear orders.empty? orders.size orders.find(...) orders.exist?(...) orders.build(attributes = {}, ...) orders.create(attributes = {}) 4.3.1.1 collection(force_reload = false) collection メソッドは、関連先のオブジェクトの全ての配列を返します。 @orders = @customer.orders 4.3.1.2 collection<<(object, …) collection<< メソッドは、呼び出すモデルの主キーに、外部キーを設定することによって、 @customer.orders << @order1 4.3.1.3 collection.delete(object, …) collection.delete メソッドは、外部キーに NULL を設定することによって @customer.orders.delete(@order1) :depend => :destroy による関連であれば、加えてオブジェクトは破壊され、 4.3.1.4 collection=objects collection= メソッドは、適切に追加することと削除することによって、 4.3.1.5 collection_singular_ids collection_singular_ids メソッドはコレクション内のオブジェクトの id の配列を返します。 @order_ids = @customer.order_ids 4.3.1.6 collection_singular_ids=ids collection_singular_ids= メソッドは、適切に追加することと削除することによって、 4.3.1.7 collection.clear collection.clear メソッドはコレクションから全てのオブジェクトを取り除きます。 4.3.1.8 collection.empty? コレクションに関連先オブジェクトが含まれていない場合、collection.empty? メソッドは true を返します。 <% if @customer.orders.empty? %> No Orders Found <% end %> 4.3.1.9 collection.size collection.size メソッドはコレクション内のオブジェクトの数を返します。 @order_count = @customer.orders.size 4.3.1.10 collection.find(…) collection.find メソッドは、コレクション内のオブジェクトを見つけます。 @open_orders = @customer.orders.find(:all, :conditions => "open = 1") 4.3.1.11 collection.exist?(…) collection.exist? メソッドは、コレクション内に、 4.3.1.12 collection.build(attributes = {}, …) collection.build メソッドは関連先の型の新しいオブジェクトを一つ以上返します。 @order = @customer.orders.build(:order_date => Time.now, :order_number => "A12345") 4.3.1.13 collection.create(attributes = {}) collection.createメソッドは関連先の型の新しいオブジェクトを返します。 @order = @customer.orders.create(:order_date => Time.now, :order_number => "A12345") 4.3.2 has_many のオプション 多くの状況で、カスタマイズすること無しに has_many のデフォルトの動作を使用できます。 class Customer < ActiveRecord::Base has_many :orders, :dependent => :delete_all, :validate => :false end has_many アソシエーションはこれらのオプションをサポートしています。
4.3.2.1 :as :as オプションの設定は、 polymorphic アソシエーションだということを示します。 4.3.2.2 :autosave :autosave オプションが true に設定されているなら、 4.3.2.3 :class_name もう一方のモデルの名前が、アソシエーションの名前から得られない場合、 class Customer < ActiveRecord::Base has_many :orders, :class_name => "Transaction" end 4.3.2.4 :conditions :conditions オプションは関連先のオブジェクトが満たす必要がある制約を指定するのに使います。 class Customer < ActiveRecord::Base has_many :confirmed_orders, :class_name => "Order", :conditions => "confirmed = 1" end また、ハッシュを介して制約を設定することも出来ます。 class Customer < ActiveRecord::Base has_many :confirmed_orders, :class_name => "Order", :conditions => { :confirmed => true } end ハッシュ形式の :conditions オプションを使うなら、このアソシエーションを介したレコード作成は、 4.3.2.5 :counter_sql 通常 Rails は自動的に、関連したメンバを数える適切なSQLを生成します。 :finder_sql を指定して、 :counter_sql を指定しなかったら、 4.3.2.6 :dependent :depend オプションに :destroy を設定すると、このオブジェクトを削除すると関連先のオブジェクトを削除するために このアソシエーションに :through オプションを使っていた場合、このオプションは無視されます。 4.3.2.7 :extend :extend オプションは、アソシエーションのプロキシを拡張するために名前付きモジュールを指定します。 4.3.2.8 :finder_sql 通常 Rails は自動的に、関連したメンバを取得する適切な SQL を生成します。 4.3.2.9 :foreign_key 規約により、 Rails はもう一方のモデルの外部キーを保持するために使われるカラム名は、 class Customer < ActiveRecord::Base has_many :orders, :foreign_key => "cust_id" end いずれにせよ、Railsは外部キーカラムを作成しないでしょう。 4.3.2.10 :group :group オプションは、ファインダー SQL の中で GROUP BY 句を使い、結果をグループ分けするために、属性名を供給します。 class Customer < ActiveRecord::Base has_many :line_items, :through => :orders, :group => "orders.id" end 4.3.2.11 :include このアソシエーションが使われた時に Eager Loading されるよう二次アソシエーションを指定するために :include オプションを使うことが出来ます。 class Customer < ActiveRecord::Base has_many :orders end class Order < ActiveRecord::Base belongs_to :customer has_many :line_items end class LineItem < ActiveRecord::Base belongs_to :order end 頻繁に顧客から直接明細を取り出すなら、(@customer.orders.line_items) class Customer < ActiveRecord::Base has_many :orders, :include => :line_items end class Order < ActiveRecord::Base belongs_to :customer has_many :line_items end class LineItem < ActiveRecord::Base belongs_to :order end 4.3.2.12 :limit :limit オプションは、アソシエーションを通じて取得するオブジェクトの合計数を制限します。 class Customer < ActiveRecord::Base has_many :recent_orders, :class_name => "Order", :order => "order_date DESC", :limit => 100 end 4.3.2.13 :offset :offset オプションはアソシエーションを介してオブジェクトを取得するための始めるオフセットを指定します。 4.3.2.14 :order :order オプションは関連先オブジェクトの受け取る順番を指示します。( SQL の ORDER BY 句の構文が使われます。) class Customer < ActiveRecord::Base has_many :orders, :order => "date_confirmed DESC" end 4.3.2.15 :primary_key 規約により、 Rails はカラムがこの関連先の主キーを保持するのに id を使っていると推測します。 4.3.2.16 :readonly :readonly オプションを true に設定すると、関連先のオブジェクトをこのアソシエーション経由で取得するとき読み込み専用になるでしょう。 4.3.2.17 :select :select オプションは、関連先オブジェクトについてのデータを取得するために使われる SQL SELECT 句をオーバーライドすることになるでしょう。 自身の :select を指定するなら、関連先モデルの主キーと外部キーカラムを含めるようにしてください。 4.3.2.18 :source :source オプションは has_many :through アソシエーションの元になるアソシエーション名を指定します。 元のアソシエーションの名前が自動的にアソシエーション名から推測できない場合にのみ、このオプションを使用する必要があります。 4.3.2.19 :source_type :source_type オプションは polymorphic アソシエーションを通じて進める has_many :through アソシエーションの元になる型を指定します。 4.3.2.20 :through :through オプションは、それを通じてクエリを実行する結合モデルを指定します。 4.3.2.21 :uniq :uniq => true を指定すると、コレクションから重複を取り除きます。 class Person < ActiveRecord::Base has_many :readings has_many :posts, :through => :readings end person = Person.create(:name => 'john') post = Post.create(:name => 'a1') person.posts << post person.posts << post person.posts.inspect # => [#<Post id: 5, name: "a1">, #<Post id: 5, name: "a1">] Reading.all.inspect # => [#<Reading id: 12, person_id: 5, post_id: 5>, #<Reading id: 13, person_id: 5, post_id: 5>] 上記の例では、 これらのレコードは同じ post を指しているにも関わらず、2つの reading があり、 person.posts はその両方を運び出しました。 今、 :uniq を true に設定しましょう: class Person has_many :readings has_many :posts, :through => :readings, :uniq => true end person = Person.create(:name => 'honda') post = Post.create(:name => 'a1') person.posts << post person.posts << post person.posts.inspect # => [#<Post id: 7, name: "a1">] Reading.all.inspect # => [#<Reading id: 16, person_id: 7, post_id: 7>, #<Reading id: 17, person_id: 7, post_id: 7>] 上記の例では、まだ二つのreadingがあります。 4.3.2.22 :validate :validate オプションに false を設定するなら、このオブジェクトを保存するたびに 4.3.3 オブジェクトが保存されるのはいつ? has_many アソシエーションにオブジェクトを代入する場合、オブジェクトは自動的に保存されます。(その外部キーを更新するために) これらのいずれかの保存がバリデーションエラーで失敗した場合、 親オブジェクト( has_many アソシエーションを宣言しているほう)が保存されなければ( new_record? が true を返す)、 オブジェクトを保存せずに has_many アソシエーションにオブジェクトを割り当てたい場合、collection.buildメソッドを使いましょう。 4.4 has_and_belongs_to_many アソシエーションのリファレンス(has_and_belongs_to_many Association Reference) †has_and_belongs_to_many アソシエーションは他のモデルとの多対多の繋がりを作ります。 4.4.1 追加されるメソッド has_and_belongs_to_many アソシエーションを宣言する時、宣言するクラスは自動的にその関連に関係がある13のメソッドを手に入れます。
これらのメソッド全てで、collectionは has_and_belongs_to_many に第一引数として渡されるシンボルと置き換えられます。 class Part < ActiveRecord::Base has_and_belongs_to_many :assemblies end Part モデルの全てのインスタンスはこれらのメソッドを持っています: assemblies(force_reload = false) assemblies<<(object, ...) assemblies.delete(object, ...) assemblies=objects assembly_ids assembly_ids=ids assemblies.clear assemblies.empty? assemblies.size assemblies.find(...) assemblies.exist?(...) assemblies.build(attributes = {}, ...) assemblies.create(attributes = {}) 4.4.1.1 追加のカラムメソッド has_and_belongs_to_many アソシエーションの結合テーブルが二つの外部キーの他に追加カラムを持っているなら、 has_and_belongs_to_many アソシエーション内の結合テーブル上の追加属性の使用は、廃止されました。 4.4.1.2 collection(force_reload = false) collection メソッドは、関連先のオブジェクトの全ての配列を返します。 @assemblies = @part.assemblies 4.4.1.3 collection<<(object, …) collection<< メソッドは、結合テーブル内にレコードを作ることによって @part.assemblies << @assembly1 このメソッドは collection.concat や collection.push のエイリアスです。 4.4.1.4 collection.delete(object, …) collection.delete メソッドは、結合テーブルからレコードを削除することによって @part.assemblies.delete(@assembly1) 4.4.1.5 collection=objects collection= メソッドは、適切に追加することと削除することによって、 4.4.1.6 collection_singular_ids collection_singular_idsメソッドはコレクション内のオブジェクトの id の配列を返します。 @assembly_ids = @part.assembly_ids 4.4.1.7 collection_singular_ids=ids collection_singular_ids= メソッドは、適切に追加することと削除することによって、 4.4.1.8 collection.clear collection.clear メソッドは結合テーブルからカラムを削除することによって 4.4.1.9 collection.empty? コレクションに関連先オブジェクトが含まれていない場合、 collection.empty? メソッドは true を返します。 <% if @part.assemblies.empty? %> This part is not used in any assemblies <% end %> 4.4.1.10 collection.size collection.size メソッドはコレクション内のオブジェクトの数を返します。 @assembly_count = @part.assemblies.size 4.4.1.11 collection.find(…) collection.find メソッドは、コレクション内のオブジェクトを見つけます。 @new_assemblies = @part.assemblies.find(:all, :conditions => ["created_at > ?", 2.days.ago]) 4.4.1.12 collection.exist?(…) collection.exist? メソッドは、コレクション内に、 4.4.1.13 collection.build(attributes = {}) collection.build メソッドは関連先の型の新しいオブジェクトを返します。 @assembly = @part.assemblies.build( {:assembly_name => "Transmission housing"}) 4.4.1.14 collection.create(attributes = {}) collection.create メソッドは関連先の型の新しいオブジェクトを返します。 @assembly = @part.assemblies.create( {:assembly_name => "Transmission housing"}) 4.4.2 has_and_belongs_to_many のオプション 多くの状況で、カスタマイズすること無しに has_and_belongs_to_many のデフォルトの動作を使用できます。 class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :uniq => true, :read_only => true end has_and_belongs_to_many アソシエーションはこれらのオプションをサポートしています:
4.4.2.1 :association_foreign_key 規約により、 Rails はもう一方のモデルを指す外部キーを保持するために結合テーブル内で使われるカラムは、 :foreign_key と :association_foreign_key オプションは、自己結合する多対多を設定する時便利です。例えば: class User < ActiveRecord::Base has_and_belongs_to_many :friends, :class_name => "User", :foreign_key => "this_user_id", :association_foreign_key => "other_user_id" end 4.4.2.2 :autosave :autosave オプションが true に設定されているなら、 4.4.2.3 :class_name もう一方のモデルの名前が、アソシエーションの名前から得られない場合、 class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :class_name => "Gadget" end 4.4.2.4 :conditions :conditions オプションは関連先のオブジェクトが満たす必要がある制約を指定するのに使います。 class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :conditions => "factory = 'Seattle'" end また、ハッシュを介して制約を設定することも出来ます。 class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :conditions => { :factory => 'Seattle' } end ハッシュ形式の :conditions オプションを使うなら、このアソシエーションを介したレコード作成は、 4.4.2.5 :counter_sql 通常 Rails は自動的に、関連したメンバを数える適切な SQL を生成します。 :finder_sql を指定して、 :counter_sql を指定しなかったら、 4.4.2.6 :delete_sql 通常 Rails は自動的に、関連しているクラス間のリンクを取り除くのに適切な SQL を生成します。 4.4.2.7 :extend :extend オプションは、アソシエーションのプロキシを拡張するために名前付きモジュールを指定します。 4.4.2.8 :finder_sql 通常 Rails は自動的に、関連したメンバを取得する適切な SQL を生成します。 4.4.2.9 :foreign_key 規約により Rails は、このモデルを指す外部キーを保持するために結合テーブル内で使われるカラムは、 class User < ActiveRecord::Base has_and_belongs_to_many :friends, :class_name => "User", :foreign_key => "this_user_id", :association_foreign_key => "other_user_id" end 4.4.2.10 :group :group オプションは、ファインダー SQL の中で GROUP BY 句を使い、結果をグループ分けするために、 class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :group => "factory" end 4.4.2.11 :include この関連が使われた時に Eager Loading されるよう二次アソシエーションを指定するために :include オプションを使うことが出来ます。 4.4.2.12 :insert_sql 通常 Rails は自動的に、関連したクラス間のリンクを作る適切な SQL を生成します。 4.4.2.13 :join_table 結合テーブルのデフォルトの名前は、辞書順の連結に基づきますが、 4.4.2.14 :limit :limit オプションは、アソシエーションを通じて取得するオブジェクトの合計数を制限します。 class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :order => "created_at DESC", :limit => 50 end 4.4.2.15 :offset :offset オプションはアソシエーションを介してオブジェクトを取得するための始めるオフセットを指定します。 4.4.2.16 :order :order オプションは関連先オブジェクトの受け取る順番を指示します。( SQL の ORDER BY 句の構文が使われます。) class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :order => "assembly_name ASC" end 4.4.2.17 :readonly :readonly オプションを true に設定すると、関連先のオブジェクトをこのアソシエーション経由で取得するとき読み込み専用になるでしょう。 4.4.2.18 :select :select オプションは、関連先オブジェクトについてのデータを取得するために使われる SQL SELECT 句をオーバーライドすることになるでしょう。 4.4.2.19 :uniq :uniq => true を指定すると、コレクションから重複を取り除きます。 4.4.2.20 :validate :validate オプションに false を設定するなら、このオブジェクトを保存するたびに 4.4.3 オブジェクトが保存されるのはいつ? has_and_belongs_to_many アソシエーションにオブジェクトを代入する場合、オブジェクトは自動的に保存されます。(結合テーブルを更新するために) これらのいずれかの保存がバリデーションエラーで失敗した場合、 親オブジェクト( has_and_belongs_to_many アソシエーションを宣言しているほう)が保存されなければ( new_record? が true を返す)、 オブジェクトを保存せずに has_and_belongs_to_many アソシエーションにオブジェクトを割り当てたい場合、 collection.build メソッドを使いましょう。 4.5 アソシエーションのコールバック(Association Callbacks) †通常のコールバックは Active Record オブジェクトのライフサイクル内でフックし、 アソシエーションのコールバックは通常のコールバックと似ていますが、
アソシエーションの宣言にオプションを追加することによって、アソシエーションのコールバックを定義します。 class Customer < ActiveRecord::Base has_many :orders, :before_add => :check_credit_limit def check_credit_limit(order) ... end end Rails はコールバックに追加または削除されたオブジェクトを渡します。 1つのイベントに配列として渡すことによってコールバックをスタックすることができます: class Customer < ActiveRecord::Base has_many :orders, :before_add => [:check_credit_limit, :calculate_shipping_charges] def check_credit_limit(order) ... end def calculate_shipping_charges(order) ... end end before_add コールバックが例外を投げたなら、オブジェクトはコレクションに追加されません。 4.6 アソシエーションの拡張(Association Extensions) †アソシエーションのプロキシオブジェクト内に、Rails が自動的にビルドする機能を限定されません。 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 find(:all, :conditions => ["created_at > ?", 5.days.ago]) end end class Customer < ActiveRecord::Base has_many :orders, :extend => FindRecentExtension end class Supplier < ActiveRecord::Base has_many :deliveries, :extend => FindRecentExtension end 一つのアソシエーション内で一つ以上の拡張モジュールを呼び出すなら、モジュールの配列を指定をしてください: class Customer < ActiveRecord::Base has_many :orders, :extend => [FindRecentExtension, FindActiveExtension] end 拡張はアソシエーションのプロキシの内部を、以下の3つのアクセサを使って参照できます。:
5 Changelog †
コメント欄(誤訳・誤字があれば教えて頂ければ幸いです。) † |