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 アソシエーション機能を説明します。
このガイドを参照することで、以下のことができるようになります:

  • Active Record のモデル間のアソシエーションの宣言
  • Active Record アソシエーションの様々な種類への理解
  • アソシエーションの作成によってモデルに追加されるメソッドの利用

1 何故アソシエーションなの?(Why Associations?)

何故モデル間にアソシエーションが必要なのでしょう?
それは、コード内の一般的な操作を単純で簡単にするからです。
例えば、 顧客のためのモデルと注文のためのモデルが含まれている簡単なRailsアプリケーションを考えて下さい。
各顧客は、多くの注文をすることができます。
アソシエーションなしでは、モデルの宣言はこのようになります:

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 のアソシエーションで、これらを合理化するほかに、
Rails に2つのモデル間の繋がりがあると宣言することによって、その他の操作ができます。
顧客と注文を設定するための修正したコードがあります:

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

アソシエーションの他の種類の詳細を学ぶために、このガイドの次のセクションを読んで下さい。
アソシエーションで動く tips や trick 、そして Rails 内のアソシエーションに関するメソッドとオプションの完全なリファレンスが続きます。

2 アソシエーションの種類(The Types of Associations)

Rails では、アソシエーションは二つの Active Record モデルの間の繋がりです。
宣言するようにモデルに機能を追加できるよに、アソシエーションはマクロスタイルの呼び出しを使用して実装されています。
例えば、あるモデルで、 belongs_to 他のモデル と宣言すれば、二つのモデルのインスタンス間の主キー-外部キー情報を維持しなさいとRailsに指示します。
また、モデルにユーティリティメソッドをいくつか追加したことになります。
Rails は6種類の関連をサポートしています。

  • belongs_to
  • has_one
  • has_many
  • has_many :through
  • has_one :through
  • has_and_belongs_to_many

このガイドの残りの部分では、宣言方法と、アソシエーションの様々な形式の使用方法を学びます。
ですが最初に、各アソシエーションのタイプが適切である状況をササッと紹介します。

2.1 belongs_to アソシエーション(The belongs_to Association)

belongs_to アソシエーションは、宣言したモデルの全てのインスタンスがもう一方のモデルのインスタンスの一つに "belongs_to (属する)" ように、
もう一方のモデルとの1対1の繋がりを設定します。
例えば、アプリケーションが顧客( customer )と注文( order )を含む場合、各注文に正確に一人の顧客を割り当てることができます、注文モデルでこのように宣言します:

class Order < ActiveRecord::Base
  belongs_to :customer
end
belongs_to アソシエーションのダイアグラム

2.2 has_one アソシエーション(The has_one Association)

has_one アソシエーションもまた、もう一方のモデルとの1対1の繋がりを設定します、が、少しだけ意味(と結果)が違います。
このアソシエーションは、モデルの各インスタンスが別のモデルのインスタンスの1つを格納しているか、持っていることを示します。
例えば、 アプリケーションで各業者( supplier )は1つだけ勘定( account )を持っている場合、このような業者モデルを宣言するでしょう。

class Supplier < ActiveRecord::Base
  has_one :account
end
has_one アソシエーションのダイアグラム

2.3 has_many アソシエーション(The has_many Association)

has_many アソシエーションは、もう一方のモデルとの1対多の繋がりを示します。
belongs_to 関連の "逆側" にこのアソシエーションをよく見るでしょう。
このアソシエーションは、そのモデルの各インスタンスが別のモデルの0以上のインスタンスを持っていることを示しています。
たとえば、顧客と注文を含むアプリケーションでは、顧客モデルはこのように宣言することができます:

class Customer < ActiveRecord::Base
  has_many :orders
end

もう一方のモデルの名前は、has_many 関連の宣言時に複数形にされます。

has_many アソシエーションのダイアグラム

2.4 has_many :through アソシエーション(The has_many :through Association)

has_many :through アソシエーションは、もう一方のモデルとの多対多の繋がりを設定するのによく使われます。
このアソシエーションは、第三のモデルを介することにより、宣言したモデルがもう一方のモデルの0以上のインスタンスとマッチすることを示します。
たとえば、患者( patient )が医師( physician )と会う予約( appointment )をとるための医療業務を考えます。
その関連するアソシエーションの宣言はこのようにできます。

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
has_many :through アソシエーションのダイアグラム

結合モデルのコレクションは、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の繋がりを設定します。
このアソシエーションは、第三のモデルを介することにより、宣言したモデルがもう一方のモデルのインスタンス1つとマッチすることを示します。
例えば、 各業者( supplier )は1つだけ勘定( account )を持っている場合、
各勘定は1つの勘定履歴( account_history )と関連付けられているならば、顧客モデルは、このようになります:

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
has_one :through アソシエーションのダイアグラム

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
has_and_belongs_to_many アソシエーションのダイアグラム

2.7 belongs_to と has_one の選択(Choosing Between belongs_to and has_one)

二つのモデル間の1対1の関係を設定したければ、
belongs_to を一方のモデルに加え、もう一方に has_one を加える必要があるでしょう。
どっちがどっちだと、どうやって知るのでしょう?

区別は、どちらに外部キーを配置するかです。(外部キーはbelongs_to アソシエーションを宣言したクラスのテーブルに配置されます。)
が、データだけでなく、実際の意味にいくらか考える必要があります。
has_one アソシエーションは、何か一つはあなたのものである - つまり、何かがあなたを指し示しているということです。
例えば、勘定が業者を所有しているというよりも、業者が勘定を所有しているというほうが、理に叶っています。
これは正しい関係が以下のようなことを示唆します:

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 を使うことで外部キーの名前は明らかで明示的なものに出来ます。
Rails の現在のバージョンでは、 t.references :supplier を代わりに使うことで、この実装の詳細を抽象化することができます

2.8 has_many :through と has_and_belongs_to_many の選択(Choosing Between has_many :through and has_and_belongs_to_many)

Rails は、2つの異なる方法をモデル間の多対多の関係を宣言するために提供しています。
簡単な方法は、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

多対多を宣言する二番目の方法は、 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 関連を設定すべきだということです。
関連モデルが何もする必要が無いなら、 has_and_belongs_to_many アソシエーションを設定する方がシンプルかもしれません。
(ですが、データベースに結合テーブルを作成する必要があることを覚えておく必要があるでしょう。)

バリデーション、コールバック、結合モデル上の追加の要素、が必要であれば、 has_many :through を使うべきでしょう。

2.9 polymorphic アソシエーション(Polymorphic Associations)

アソシエーション上のちょっと高度な応用は、 polymorphic アソシエーションです。
polymorphic アソシエーションにより、一つのアソシエーション上で、モデルは1つ以上の他のモデルに属することができます。
例えば、従業員( employee )か製品( product )モデルに属している、写真( picture )モデルがあるかもしれません。
こういうふうに宣言します。

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 宣言を、他のモデルが利用出来るインターフェイスの設定として考えることができます。
Employee モデルのインスタンスから、 picture のコレクションを @employee.pictures で取得することができます。

同様に、 @product.pictures で取得することもできます。

Picture モデルのインスタンスがあるなら、 @picture.imageable を通じてその親を取得することもできます。
この動作のために、両方の外部キーカラムと polymorphic インターフェイスを宣言するモデルのカラムの型を宣言する必要があります:

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
polymorphic アソシエーションのダイアグラム

2.10 自己結合(Self Joins)

データモデルの設計をしていると、時々、自己関連する必要があるモデルを見つけるでしょう。
例えば、1つのデータベースのモデル内にすべての従業員を格納するが、
マネージャーと、部下( subordinate )の間の関係をトレースすることが出来るようにしたいかもしれません。
このシチュエーションでは、自己結合 アソシエーションをモデル化することができます。

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 のキャッシュコピーを使う

データはアプリケーションの他の部分で変更されているかもしれないので、もしキャッシュを再読み込みしたいなら?
ただ、そのアソシエーションの呼び出しに true を渡すだけです:

customer.orders               # データベースからordersを取得
customer.orders.size          # ordersのキャッシュコピーを使う
customer.orders(true).empty?  # ordersのキャッシュコピーを捨てる
                              # 再度データベースから取得する

3.2 名前の衝突を回避する(Avoiding Name Collisions)

アソシエーションのために好きな名前を使っていいわけではありません。
アソシエーションの作成は、モデルにその名前のメソッドを追加するので、 ActiveRecord::Base のインスタンスメソッドで既に使われている名前をアソシエーションに与えるのは悪い考えです。
アソシエーションのメソッドは base メソッドをオーバーライドし、物事をぶっ壊すでしょう。
例えば、 attributes や connection は、関連にとって悪い名前です。

3.3 スキーマのアップデート(Updating the Schema)

アソシエーションは非常に便利ですが、魔法ではありません。
あなたには、アソシエーションにマッチするように、データベーススキーマを維持する責任があります。
実際には、これは二つのことを意味し、あなたが作成したアソシエーションの種類に依存します。
belongs_to アソシエーションは、外部キーを作成する必要があり、 has_and_belongs_to_many アソシエーションは、適切な結合テーブルの作成が必要になります。

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

基礎となるモデルを作った後に、アソシエーションを作りたいなら、
必要な外部キーを提供するために add_column マイグレーションを作ることを覚えておく必要があります。

3.3.2 has_and_belongs_to_many アソシエーションのための結合テーブルの作成

has_and_belongs_to_many アソシエーションを作るなら、結合テーブルを明示的に作る必要があります。
:join_table オプションを使って、結合テーブルの名前を明示的に指定しなければ、
Active Record はクラス名の辞書順の連結を使って名前を作ります。
customer と order モデル間の結合は、辞書順で "c" は "o" より前なので、 "customers_orders" というデフォルトの結合テーブルの名前を与えるでしょう。

モデル名間の優先順位は、 String の < 演算子を使用して計算されます。
これは、文字列の長さが違い、短いほうの文字列の長さまで比較していって同じだった場合、
長いほうの文字列は、短いほうの文字列より辞書順で後ろだと考えられるということを意味しています。
例えば、誰かが "paper_boxes" の名前の長さから、
"paper_boxes" と "papers" テーブルは、結合テーブル名を "papers_paper_boxes" とすると期待するでしょう。
が、実際に作られる結合テーブルの名前は、 "paper_boxes_papers" です。
(なぜなら、一般的なエンコードでアンダースコア '_' は 's' より辞書的に前だからです。)

どのような名前かに関わらず、適切なマイグレーションで結合テーブルを手動で生成する必要があります。
例えば、このようなアソシエーションを考えましょう。

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 を渡します。
それはアソシエーションが正常に動作するために必要です。
破損したモデルの ID や、 ID の競合についての例外のように
has_and_belongs_to_many アソシエーションの奇妙な振る舞いを観察したら、これを思い出す機会です。

3.4 アソシエーションのスコープのコントロール(Controlling Association Scope)

デフォルトでは、アソシエーションは現在のモジュールのスコープ内でだけ、オブジェクトを探します。
モジュール内で Active Record モデルの宣言をしているとき、このことは重要になりえます。
例えば:

module MyApplication
  module Business
    class Supplier < ActiveRecord::Base
      has_one :account
    end

    class Account < ActiveRecord::Base
      belongs_to :supplier
    end
  end
end

これは、正常に動作します。なぜなら、 Supplier と Account クラスはどちらも同じスコープで定義されているからです。
しかし、次はうまく動作しません。なぜなら、 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の繋がりを作ります。
データベースにおいて、このクラスが外部キーを格納していることを、このアソシエーションが示しています。
もう一方のクラスが外部キーがを格納している場合、代わりに has_one を使うべきでしょう。

4.1.1 belongs_to により追加されたメソッド

belongs_to アソシエーションを宣言するとき、宣言クラスは、自動的にそのアソシエーションに関する4つのメソッドを得ます。

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

これら全てのメソッドで、 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 メソッドは、もしあれば関連するオブジェクトを返します。
関連するオブジェクトが見つからない場合、 nil を返します。

@customer = @order.customer

関連先のオブジェクトが既にこのオブジェクトのためにデータベースから取得されている場合、キャッシュされたバージョンが返されるでしょう。
この動作をオーバーライドする(強制的にデータベースを読み込む)には、 force_reload 引数に true を渡してください。

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 のデフォルトの動作を使用できます。
しかし、 Rails の重点である、設定よりも規約にも関わらず、いくつかの方法でその動作を変更することができます。
belongs_to アソシエーションを作成するとき、渡すことができるオプションを、このセクションでカバーしています。
例えば、いくつかのオプションとアソシエーションはこのようになるでしょう:

class Order < ActiveRecord::Base
  belongs_to :customer, :counter_cache => true,
    :conditions => "active = 1"
end

belongs_to アソシエーションはこれらのオプションをサポートしています:

  • :autosave
  • :class_name
  • :conditions
  • :counter_cache
  • :dependent
  • :foreign_key
  • :include
  • :polymorphic
  • :readonly
  • :select
  • :touch
  • :validate

4.1.2.1 :autosave

:autosave オプションが true に設定されているなら、
親オブジェクトを保存するたびに、ロードされたメンバは保存され、破壊フラグがたっているメンバは破壊されるでしょう。

4.1.2.2 :class_name

もう一方のモデルの名前が、アソシエーションの名前から得られない場合、
:class_name オプションを、モデルの名前を供給するために使えるでしょう。
例えば、注文が顧客に属しているが、実際に顧客を含むモデルの名前は Patron の場合、このように設定できます:

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

4.1.2.3 :conditions

:conditions オプションは、関連先のオブジェクトが満たす必要がある制約を指定するのに使います。
(SQL文のWHERE句で使用される構文で。)

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 宣言を含むモデル上で指定されますが、実際のカラムは、関連先のモデルに追加する必要があります。
上記の場合では、 Customer モデルに orders_count という名前のカラムを追加する必要があります。
必要な場合は、デフォルトのカラム名をオーバーライドできます。

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 を設定したなら、このオブジェクトを削除すると、
関連先のオブジェクトを削除するために、関連先のオブジェクトの destroy メソッドが呼ばれるでしょう。
:depend オプションに :delete を設定したなら、このオブジェクトを削除すると、
関連先のオブジェクトの destroy メソッドを呼ばずに、関連先のオブジェクトが削除されるでしょう。

もう一方のクラスとhas_many アソシエーションで繋がっている、belongs_to アソシエーションにこのオプションを指定しないほうがよいでしょう。
そうすることは、データベースのレコードが孤立することにつながるでしょう。

4.1.2.6 :foreign_key

規約により Rails は、このモデルの外部キーを保持するために使われるカラム名は、関連先のモデル名のお尻に_idを付けたものだと推測します。
:foreign_key オプションは外部キーの名前を直接設定します:

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 を即時的なアソシエーションに使う必要はありません。
つまり、 Order belongs_to :customer があるなら、必要な時に自動的に顧客が Eager Loading されます。

4.1.2.8 :polymorphic

:polymorphic オプションに true を渡すと、これが polymorphic アソシエーションだということを示します。
polymorphic アソシエーションは、このガイドの前のほうで議論されています。

4.1.2.9 :readonly

:readonly オプションを true に設定すると、関連先のオブジェクトをそのアソシエーション経由で取得するとき、読み込み専用になるでしょう。

4.1.2.10 :select

:select オプションは、関連先オブジェクトについてのデータを取得するために使われる SQL SELECT 句をオーバーライドすることになるでしょう。

belongs_to アソシエーション上で :select オプションを設定するなら、
正しい結果を保証するために :foreign_key オプションを設定するべきでしょう。

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 を設定するなら、このオブジェクトを保存するたびに、関連先のオブジェクトはバリデーションされるでしょう。
デフォルトでは、これは false になっています:
このオブジェクトが保存されるときに関連先オブジェクトはバリデーションされないでしょう。

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 アソシエーションは、他のモデルとの一対一の繋がりを作ります。
データベースにおいて、このアソシエーションは、もう一方のクラスが外部キーを持っていることを表します。
このクラスが外部キーを含む場合、belongs_to を代わりに使うべきでしょう。

4.2.1 has_one によって追加されるメソッド

has_one アソシエーションを宣言する時、宣言するクラスは自動的にそのアソシエーションに関係がある4つのメソッドを手に入れます:

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

これらのメソッド全てで、 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 メソッドは、もしあれば関連するオブジェクトを返します。
関連するオブジェクトが見つからない場合、 nil を返します。

@account = @supplier.account

関連先のオブジェクトが既にこのオブジェクトのデータベースから取得されている場合、キャッシュされたバージョンが返されるでしょう。
この動作をオーバーライドする(強制的にデータベースを読み込む)には、 force_reload 引数に true を渡してください。

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 のデフォルトの動作を使用できます。
しかし、Railsの重点である、設定よりも規約にも関わらず、いくつかの方法でその動作を変更することができます。
has_one アソシエーションを作成するとき、渡すことができるオプションを、このセクションでカバーしています。
例えば、いくつかのオプションとアソシエーションはこのようになるでしょう。

class Supplier < ActiveRecord::Base
  has_one :account, :class_name => "Billing", :dependent => :nullify
end

has_one アソシエーションはこれらのオプションをサポートしています。

  • :as
  • :autosave
  • :class_name
  • :conditions
  • :dependent
  • :foreign_key
  • :include
  • :order
  • :primary_key
  • :readonly
  • :select
  • :source
  • :source_type
  • :through
  • :validate

4.2.2.1 :as

:as オプションの設定は、 polymorphic アソシエーションだということを示します。
polymorphic アソシエーションは、このガイドの前のほうで議論されています。

4.2.2.2 :autosave

:autosave オプションが true に設定されているなら、
親オブジェクトを保存するたびに、ロードされたメンバは保存され、破壊フラグがたっているメンバは破壊されるでしょう。

4.2.2.3 :class_name

もう一方のモデルの名前が、アソシエーションの名前から得られない場合、 :class_name オプションを、モデルの名前を供給するために使えるでしょう。
例えば、業者( supplier )が勘定( account )を持っているが、実際に勘定を含むモデルの名前は Billing の場合、このように設定できます:

class Supplier < ActiveRecord::Base
  has_one :account, :class_name => "Billing"
end

4.2.2.4 :conditions

:conditions オプションは関連先のオブジェクトが満たす必要がある制約を指定するのに使います。
(SQL文のWHERE句で使用される構文で。)

class Supplier < ActiveRecord::Base
  has_one :account, :conditions => "confirmed = 1"
end

4.2.2.5 :dependent

:depend オプションに :destroy を設定すると、このオブジェクトを削除すると関連先のオブジェクトを削除するために
関連先のオブジェクトの destroy メソッドが呼ばれるでしょう。
:depend オプションに :delete を設定すると、このオブジェクトを削除すると関連先のオブジェクトの destroy メソッドを呼ばずに
関連先のオブジェクトが削除されるでしょう。 :depend オプションに :nullify を設定すると、このオブジェクトを削除すると関連先のオブジェクトの
外部キーが NULL に設定されるでしょう。

4.2.2.6 :foreign_key

規約により、 Rails はもう一方のモデルの外部キーを保持するために使われるカラム名は、このモデル名のお尻に_idを付けたものだと推測します。
:foreign_key オプションは外部キーの名前を直接設定します:

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)
業者から勘定( account )のアソシエーションに代表者を含めることによって、もっと効率的なコードに出来ます。

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 句の構文が使われます。)
has_one アソシエーションは一つの関連先オブジェクトしか受け取らないので、このオプションは必要ないでしょう。

4.2.2.9 :primary_key

規約により Rails は、カラムがこのモデルの主キーを保持するのに id を使っていると推測します。
:primary_key オプションを使うことにより、これを上書きし、明示的に主キーを指定できます。

4.2.2.10 :readonly

:readonly オプションを true に設定すると、関連先のオブジェクトをこのアソシエーション経由で取得するとき読み込み専用になるでしょう。

4.2.2.11 :select

:select オプションは、関連先オブジェクトについてのデータを取得するために使われる SQL SELECT 句をオーバーライドすることになるでしょう。
デフォルトでは Rails は全てのカラムを取得します。

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 オプションは、それを通じてクエリを実行する結合モデルを指定します。
has_one :through アソシエーションは、このガイドの前のほうで議論されています。

4.2.2.15 :validate

:validate オプションに true を設定するなら、このオブジェクトを保存するたびに、関連先のオブジェクトはバリデーションされるでしょう。
デフォルトでは、これは false になっています:
このオブジェクトが保存されるときに関連先オブジェクトはバリデーションされないでしょう。

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(force_reload = false)
  • collection<<(object, …)
  • collection.delete(object, …)
  • collection=objects
  • collection_singular_ids
  • collection_singular_ids=ids
  • collection.clear
  • collection.empty?
  • collection.size
  • collection.find(…)
  • collection.exist?(…)
  • collection.build(attributes = {}, …)
  • collection.create(attributes = {})

これらのメソッド全てで、 collection は has_many に第一引数として渡されるシンボルと置き換えられます。
またcollection_singularはシンボルを単数化したバージョンと置き換えられます。
例えば、宣言を与えます:

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 による関連であれば、加えてオブジェクトは破壊され、
:depend => :delete_all による関連であれば、加えてオブジェクトは削除されます。

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 メソッドはコレクションから全てのオブジェクトを取り除きます。
これは、 :dependent => :destroy 関連であれば、関連先のオブジェクトを破壊し、
:dependent => :delete_all 関連であれば、データベースから直接削除し、
その他であれば、外部キーに NULL を設定します。

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 メソッドは、コレクション内のオブジェクトを見つけます。
これは、 ActiveRecord::Base.find と似たような構文とオプションを使います。

@open_orders = @customer.orders.find(:all, :conditions => "open = 1")

4.3.1.11 collection.exist?(…)

collection.exist? メソッドは、コレクション内に、
指定された制約を満たすオブジェクトが存在するかどうかをチェックします。
これは、 ActiveRecord::Base.exists? と似たような構文とオプションを使います。

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 のデフォルトの動作を使用できます。
しかし、いくつかの方法でその動作を変更することができます。
has_many アソシエーションを作成するとき、渡すことができるオプションを、このセクションでカバーしています。
例えば、いくつかのオプションとアソシエーションはこのようになるでしょう。

class Customer < ActiveRecord::Base
  has_many :orders, :dependent => :delete_all, :validate => :false
end

has_many アソシエーションはこれらのオプションをサポートしています。

  • :as
  • :autosave
  • :class_name
  • :conditions
  • :counter_sql
  • :dependent
  • :extend
  • :finder_sql
  • :foreign_key
  • :group
  • :include
  • :limit
  • :offset
  • :order
  • :primary_key
  • :readonly
  • :select
  • :source
  • :source_type
  • :through
  • :uniq
  • :validate

4.3.2.1 :as

:as オプションの設定は、 polymorphic アソシエーションだということを示します。
polymorphic アソシエーションは、このガイドの前のほうで議論されています。

4.3.2.2 :autosave

:autosave オプションが true に設定されているなら、
親オブジェクトを保存するたびに、ロードされたメンバは保存され、破壊フラグがたっているメンバは破壊されるでしょう。

4.3.2.3 :class_name

もう一方のモデルの名前が、アソシエーションの名前から得られない場合、
:class_name オプションを、モデルの名前を供給するために使えるでしょう。
例えば、顧客が注文を持っているが、実際に注文を含むモデルの名前は Transaction の場合、
このように設定できます。

class Customer < ActiveRecord::Base
  has_many :orders, :class_name => "Transaction"
end

4.3.2.4 :conditions

:conditions オプションは関連先のオブジェクトが満たす必要がある制約を指定するのに使います。
(SQL文のWHERE句で使用される構文で。)

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 オプションを使うなら、このアソシエーションを介したレコード作成は、
ハッシュを使ったスコープに自動的になるでしょう。
この場合なら、 @customer.confirmed_orders.create か @customer.confirmed_orders.build を使うことは、
confirmed カラムが true である場合という命令を作るでしょう。

4.3.2.5 :counter_sql

通常 Rails は自動的に、関連したメンバを数える適切なSQLを生成します。
:counter_sql オプションは、数える為の完全なSQL文を自身で指定できます。

:finder_sql を指定して、 :counter_sql を指定しなかったら、
:finder_sql ステートメントの SELECT COUNT(*) FROM for the SELECT ... FROM 句に置換することによって
カウンタSQLが生成されるでしょう。

4.3.2.6 :dependent

:depend オプションに :destroy を設定すると、このオブジェクトを削除すると関連先のオブジェクトを削除するために
関連先のオブジェクトのdestroyメソッドが呼ばれるでしょう。
:depend オプションに :delete_all を設定すると、このオブジェクトを削除すると関連先のオブジェクトの destroy メソッドを呼ばずに
関連先のオブジェクトが削除されるでしょう。 :dependオプションに :nullify を設定すると、このオブジェクトを削除すると関連先のオブジェクトの外部キーが
NULL に設定されるでしょう。

このアソシエーションに :through オプションを使っていた場合、このオプションは無視されます。

4.3.2.7 :extend

:extend オプションは、アソシエーションのプロキシを拡張するために名前付きモジュールを指定します。
アソシエーションの拡張については、このガイドの後のほうで議論されています。

4.3.2.8 :finder_sql

通常 Rails は自動的に、関連したメンバを取得する適切な SQL を生成します。
:finger_sql オプションは、取得するための完全な SQL 文を自身で指定できます。
オブジェクトを取得するのに、複雑なマルチテーブル SQL を必要とする場合、これが必要になるかもしれません。

4.3.2.9 :foreign_key

規約により、 Rails はもう一方のモデルの外部キーを保持するために使われるカラム名は、
このモデル名のお尻に _id を付けたものだと推測します。
:foreign_key オプションは外部キーの名前を直接設定します:

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 オプションはアソシエーションを介してオブジェクトを取得するための始めるオフセットを指定します。
例えば、 :offset => 11 を設定すれば、最初の 11 のレコードをスキップするでしょう。

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 を使っていると推測します。
:primary_key オプションを使うことにより、これを上書きし、明示的に主キーを指定できます。

4.3.2.16 :readonly

:readonly オプションを true に設定すると、関連先のオブジェクトをこのアソシエーション経由で取得するとき読み込み専用になるでしょう。

4.3.2.17 :select

:select オプションは、関連先オブジェクトについてのデータを取得するために使われる SQL SELECT 句をオーバーライドすることになるでしょう。
デフォルトでは Rails は全てのカラムを受け取ります。

自身の :select を指定するなら、関連先モデルの主キーと外部キーカラムを含めるようにしてください。
そうしなければ Rails はエラーを投げるでしょう。

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 オプションは、それを通じてクエリを実行する結合モデルを指定します。
多対多の実装をするための has_many :through アソシエーションは、このガイドの前のほうで議論されています。

4.3.2.21 :uniq

:uniq => true を指定すると、コレクションから重複を取り除きます。
:through オプションと使うのは、最も便利な組み合わせです。

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があります。
しかしながら、コレクションは一意なレコードだけロードするので、 person.posts は一つの post しか表示していません。

4.3.2.22 :validate

:validate オプションに false を設定するなら、このオブジェクトを保存するたびに
関連先のオブジェクトはバリデーションされないでしょう。
デフォルトでは、これは true になっています:
このオブジェクトが保存されるときに関連先オブジェクトはバリデーションされるでしょう。

4.3.3 オブジェクトが保存されるのはいつ?

has_many アソシエーションにオブジェクトを代入する場合、オブジェクトは自動的に保存されます。(その外部キーを更新するために)
一つのステートメントで複数のオブジェクトを代入する場合、全て保存されます。

これらのいずれかの保存がバリデーションエラーで失敗した場合、
代入文は false を返し、代入自体はキャンセルされます。

親オブジェクト( 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(force_reload = false)
  • collection<<(object, …)
  • collection.delete(object, …)
  • collection=objects
  • collection_singular_ids
  • collection_singular_ids=ids
  • collection.clear
  • collection.empty?
  • collection.size
  • collection.find(…)
  • collection.exist?(…)
  • collection.build(attributes = {})
  • collection.create(attributes = {})

これらのメソッド全てで、collectionは has_and_belongs_to_many に第一引数として渡されるシンボルと置き換えられます。
またcollection_singularはシンボルを単数化したバージョンと置き換えられます。
例えば、宣言は:

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 アソシエーションの結合テーブルが二つの外部キーの他に追加カラムを持っているなら、
これらのカラムはアソシエーションを介して取得したレコードに属性として追加されるでしょう。
追加属性とともに返されたレコードは常に読み込み専用でしょう、なぜなら Rails はこれらの属性に変更を保存できないからです。

has_and_belongs_to_many アソシエーション内の結合テーブル上の追加属性の使用は、廃止されました。
多対多関連内で、この種の、二つのモデルを結合するテーブル上の複雑な振る舞いを要求するなら、
has_many :through アソシエーションを 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 メソッドは、コレクション内のオブジェクトを見つけます。
これは、 ActiveRecord::Base.find と似たような構文とオプションを使います。
また、制約を追加することができ、そのオブジェクトはコレクション内にある必要があります。

@new_assemblies = @part.assemblies.find(:all,  :conditions => ["created_at > ?", 2.days.ago])

4.4.1.12 collection.exist?(…)

collection.exist? メソッドは、コレクション内に、
指定された制約を満たすオブジェクトが存在するかどうかをチェックします。
これは、 ActiveRecord::Base.exists? と似たような構文とオプションを使います。

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 のデフォルトの動作を使用できます。
しかし、いくつかの方法でその動作を変更することができます。
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 アソシエーションはこれらのオプションをサポートしています:

  • :association_foreign_key
  • :autosave
  • :class_name
  • :conditions
  • :counter_sql
  • :delete_sql
  • :extend
  • :finder_sql
  • :foreign_key
  • :group
  • :include
  • :insert_sql
  • :join_table
  • :limit
  • :offset
  • :order
  • :readonly
  • :select
  • :uniq
  • :validate

4.4.2.1 :association_foreign_key

規約により、 Rails はもう一方のモデルを指す外部キーを保持するために結合テーブル内で使われるカラムは、
モデル名のお尻 に_id を付けたものだと推測します。
:association_foreign_key オプションは外部キーの名前を直接設定できます。

: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_name オプションを、モデルの名前を供給するために使えるでしょう。
例えば、パーツがアセンブリ(組立部品)を持っているが、実際にアセンブリを含むモデルの名前は Gadget の場合、
このように設定できます。

class Parts < ActiveRecord::Base
  has_and_belongs_to_many :assemblies, :class_name => "Gadget"
end

4.4.2.4 :conditions

:conditions オプションは関連先のオブジェクトが満たす必要がある制約を指定するのに使います。
(SQL文のWHERE句で使用される構文で。)

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 オプションを使うなら、このアソシエーションを介したレコード作成は、
ハッシュを使ったスコープに自動的になるでしょう。
この場合なら、 @parts.assemblies.create か @parts.assemblies.build を使うのは、
factory カラムが "Seattle" という値を持っている時という命令を作るでしょう。

4.4.2.5 :counter_sql

通常 Rails は自動的に、関連したメンバを数える適切な SQL を生成します。
:counter_sql オプションは、数える為の完全な SQL 文を自身で指定できます。

:finder_sql を指定して、 :counter_sql を指定しなかったら、
:finder_sql ステートメントの SELECT COUNT(*) FROM for the SELECT ... FROM 句に置換することによって
カウンタ SQL が生成されるでしょう。

4.4.2.6 :delete_sql

通常 Rails は自動的に、関連しているクラス間のリンクを取り除くのに適切な SQL を生成します。
:delete_sql オプションは、リンクを削除する為の完全な SQL 文を自身で指定できます。

4.4.2.7 :extend

:extend オプションは、アソシエーションのプロキシを拡張するために名前付きモジュールを指定します。
アソシエーションの拡張については、このガイドの後のほうで議論されています。

4.4.2.8 :finder_sql

通常 Rails は自動的に、関連したメンバを取得する適切な SQL を生成します。
:finger_sql オプションは、取得するための完全な SQL 文を自身で指定できます。
オブジェクトを取得するのに、複雑なマルチテーブル SQL を必要とする場合、これが必要になるかもしれません。

4.4.2.9 :foreign_key

規約により Rails は、このモデルを指す外部キーを保持するために結合テーブル内で使われるカラムは、
このモデル名のお尻に _id を付けたものだと推測します。
: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.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 を生成します。
:insert_sql オプションは、それらを挿入するための完全な SQL 文を自身で指定できます。

4.4.2.13 :join_table

結合テーブルのデフォルトの名前は、辞書順の連結に基づきますが、
望まない場合、 :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 オプションはアソシエーションを介してオブジェクトを取得するための始めるオフセットを指定します。
例えば、 :offset => 11 を設定すれば、最初の 11 のレコードをスキップするでしょう。

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 句をオーバーライドすることになるでしょう。
デフォルトでは Rails は全てのカラムを受け取ります。

4.4.2.19 :uniq

:uniq => true を指定すると、コレクションから重複を取り除きます。

4.4.2.20 :validate

:validate オプションに false を設定するなら、このオブジェクトを保存するたびに
関連先のオブジェクトはバリデーションされないでしょう。
デフォルトでは、これは true になっています:
このオブジェクトが保存されるときに関連先オブジェクトはバリデーションされるでしょう。

4.4.3 オブジェクトが保存されるのはいつ?

has_and_belongs_to_many アソシエーションにオブジェクトを代入する場合、オブジェクトは自動的に保存されます。(結合テーブルを更新するために)
一つのステートメントで複数のオブジェクトを代入する場合、全て保存されます。

これらのいずれかの保存がバリデーションエラーで失敗した場合、
代入文は false を返し、代入自体はキャンセルされます。

親オブジェクト( has_and_belongs_to_many アソシエーションを宣言しているほう)が保存されなければ( new_record? が true を返す)、
追加された時、子オブジェクトは保存されません。
関連先の全ての保存されていないメンバは自動的に、親オブジェクトが保存された時に、保存されるでしょう。

オブジェクトを保存せずに has_and_belongs_to_many アソシエーションにオブジェクトを割り当てたい場合、 collection.build メソッドを使いましょう。

4.5 アソシエーションのコールバック(Association Callbacks)

通常のコールバックは Active Record オブジェクトのライフサイクル内でフックし、
様々な点でこれらのオブジェクトを動作させることを許しています。
例えば、オブジェクトが保存される直前に何かを引き起こす原因に、 :before_save コールバックを使えます。

アソシエーションのコールバックは通常のコールバックと似ていますが、
コレクションのライフサイクル内のイベントをトリガーにします。
4つの利用可能な関連コールバックがあります。

  • before_add
  • after_add
  • before_remove
  • after_remove

アソシエーションの宣言にオプションを追加することによって、アソシエーションのコールバックを定義します。
例えば:

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 コールバックが例外を投げたなら、オブジェクトはコレクションに追加されません。
同様に、before_remove コールバックが例外を投げたなら、オブジェクトはコレクションから取り除かれません。

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つのアクセサを使って参照できます。:

  • proxy_owner アソシエーションの一部であるオブジェクトを返します。
  • proxy_reflection アソシエーションを説明するリフレクションオブジェクトを返します。
  • proxy_target belongs_to や has_one の関連先オブジェクトや、has_many や has_and_belongs_to_many の関連先オブジェクトのコレクションを返します。

5 Changelog

Lighthouse ticket

  • April 7, 2010: Fixed document to validate XHTML 1.0 Strict. Jaime Iniesta
  • April 19, 2009: Added :touch option to belongs_to associations by Mike Gunderloy
  • February 1, 2009: Added :autosave option Mike Gunderloy
  • September 28, 2008: Corrected has_many :through diagram, added polymorphic diagram, some reorganization by Mike Gunderloy . First release version.
  • September 22, 2008: Added diagrams, misc. cleanup by Mike Gunderloy (not yet approved for publication)
  • September 14, 2008: initial version by Mike Gunderloy (not yet approved for publication)

コメント欄(誤訳・誤字があれば教えて頂ければ幸いです。)




添付ファイル: filepolymorphic.png 7件 [詳細] filehas_one_through.png 6件 [詳細] filehas_one.png 6件 [詳細] filehas_many_through.png 6件 [詳細] filehas_many.png 8件 [詳細] filehabtm.png 7件 [詳細] filebelongs_to.png 8件 [詳細]