Activeレコードの関連付け
このガイドでは、Activeレコードの関連付けについて説明します。 このガイドを読むことで、次の事が学べるはずです。
- Activeレコードのモデル間で関連付けを定義する方法
- Activeレコードの関連付けの様々なタイプについて
- 関連付けを作ることによってモデルに追加されるメソッドの使用方法
1. 何故、関連付けを行うのか?
何故、モデル間で関連付けを行う必要があるのでしょうか? それは、コード内での共通のオペレーションをシンプルに、また簡単にするためです。 例えば、顧客(customers)と注文(orders)のモデルを持つシンプルなRailsアプリケーションがあるとします。 それぞれの顧客は多くの注文を持ちます。 関連付けが無ければ、モデルの定義は次のようになります。
class Customer < ActiveRecord::Base
end
class Order < ActiveRecord::Base
end
ここで、存在する顧客に新しい注文を追加したいとします。 すると、次のようなことを行う必要があります。
@order = Order.create(order_date: Time.now, customer_id: @customer.id)
また、顧客を削除することを考えると、同様にその顧客の注文データが全て削除されることを確認する必要があります。
@orders = Order.where(customer_id: @customer.id)
@orders.each do |order|
order.destroy
end
@customer.destroy
Activeレコードの関連付けは、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
関連付けの異なるタイプについて更に知りたければ、このガイドの次のセクションを読み進めてください。 以降は関連付けによるチップスやテクニック、そしてRailsの関連付けのためのメソッドとオプションが揃ったリファレンスになります。
2. 関連付けのタイプ
Railsでは、関連付けによって2つのActiveレコードモデルの間が繋がれます。
関連付けは、宣言によってモデルに機能を追加できるように、マクロスタイルの呼び出しを使用して実装されます。
例えばbelongs_to
の宣言によって、
Railsに2つのモデルのインスタンスの間の主キーと外部キー情報を保持するように指示します。
また、他にもモデルに追加出来る汎用的なメソッドが揃っています。
Railsは、6種の関連付けをサポートします。
- belongs_to
- has_one
- has_many
- has_many :through
- has_one :through
- has_and_belongs_to_many
以降のこのガイドでは、様々な形式の関連付けの宣言と使用方法について学んでいきます。 ただし、その前に関連付けが適切に行われる状況について紹介しておきます。
2.1 belongs_toによる関連付け
belongs_to
関連付けは、宣言されたモデルが別のモデルに"属する"という意味になり、
1対1の関係でモデル同士を繋げます。
例えば、もしアプリケーションに顧客(customers)と注文(orders)があり、
各注文は1人の個客に割り当てられるような場合、
注文(order)モデルに次のように宣言を行います。
class Order < ActiveRecord::Base
belongs_to :customer
end
belongs_to
への関連付けには、単数形の用語を使用しなければいけません。
もし、上記の例で顧客への関連付けで複数形をOrder
モデル内で使用すると、
"uninitialized constant Order::Customers"というメッセージを受け取ることになります。
これはRailsが関連付けの名前から、自動的にクラス名を判定するためです。
関連付け名を誤って複数形にしてしまうと、同様に複数形の名前のクラスだと判定されてしまいます。
対応するマイグレーションは下記のようになります。
class CreateOrders < ActiveRecord::Migration
def change
create_table :customers do |t|
t.string :name
t.timestamps
end
create_table :orders do |t|
t.belongs_to :customer
t.datetime :order_date
t.timestamps
end
end
end
2.2 has_oneによる関連付け
has_one
関連付けも、1対1でモデルを繋げるように設定するものですが、
意味(と結論)が多少異なります。
この関連付けは、モデルの各インスタンスが他のモデルのインスタンスを含み、または所有することを指し示します。
例えば、もし各サプライヤー(supplier)がアプリケーションで1つのアカウント(account)を持つとすると、
サプライヤーモデルに次のように宣言します。
class Supplier < ActiveRecord::Base
has_one :account
end
対応するマイグレーションは下記のとおりです。
class CreateSuppliers < ActiveRecord::Migration
def change
create_table :suppliers do |t|
t.string :name
t.timestamps
end
create_table :accounts do |t|
t.belongs_to :supplier
t.string :account_number
t.timestamps
end
end
end
2.3 has_manyによる関連付け
has_many
関連付けは、1対多でモデル同士を繋げるように指し示すものです。
この関連付けは、belongs_to
による関連付けの"反対側"でよく使われるものです。
この関連付けは、モデルの各インスタンスが他のモデルのインスタンスを複数(またはゼロ)持つという事を指し示します。
例えば、アプリケーションに顧客(customers)と注文(orders)があるとすると、
顧客モデルは下記のように宣言されます。
class Customer < ActiveRecord::Base
has_many :orders
end
has_many
関連付けの宣言をする際には、指定するモデルの名前は複数形にします。
対応するマイグレーションは下記のようになります。
class CreateCustomers < ActiveRecord::Migration
def change
create_table :customers do |t|
t.string :name
t.timestamps
end
create_table :orders do |t|
t.belongs_to :customer
t.datetime :order_date
t.timestamps
end
end
end
2.4 has_many :throughによる関連付け
has_many :through
関連付けは、多対多の関係でモデルと関連付けるのに使用されます。
この関連付けが宣言されたモデルが、他の0個以上のインスタンスと、第3のモデルを通して(through)紐付けられることを指し示します。
例えば、患者(patients)が医師(physicians)を確認して予約(appointments)を行う医療の現場のシステムについて考えてみます。
この場合、関連付けの宣言は下記のようになります。
#医師クラス
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
対応するマイグレーションは下記のようになります。
class CreateAppointments < ActiveRecord::Migration
def change
create_table :physicians do |t|
t.string :name
t.timestamps
end
create_table :patients do |t|
t.string :name
t.timestamps
end
create_table :appointments do |t|
t.belongs_to :physician
t.belongs_to :patient
t.datetime :appointment_date
t.timestamps
end
end
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
through: :sections
を指定することで、Railsは関係性を把握します。
@document.paragraphs
2.5 has_one :throughによる関連付け
has_one :through
の関連付けは、1対1の関係で他のモデルを関連付けるのに使用されます。
この関連付けが宣言されたモデルが、他のモデルの1つのインスタンスと、第3のモデルを通して(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
このためのマイグレーションは、次のようになります。
class CreateAccountHistories < ActiveRecord::Migration
def change
create_table :suppliers do |t|
t.string :name
t.timestamps
end
create_table :accounts do |t|
t.belongs_to :supplier
t.string :account_number
t.timestamps
end
create_table :account_histories do |t|
t.belongs_to :account
t.integer :credit_rating
t.timestamps
end
end
end
2.6 has_and_belongs_to_many関連付け
has_and_belongs_to_many
の関連付けは直接、介在するモデル無しで、他のモデルとの多対多の繋がりを作成します。
例えば、もしあなたのアプリケーションが、"組み立て部品"(assemblies)と"パーツ"(parts)を持ち、
各組み立て部品は多くのパーツを持ち、各パーツは多くの組み立て部品で使用されるとする場合、
これを次のように宣言することが可能です。
class Assembly < ActiveRecord::Base
has_and_belongs_to_many :parts
end
class Part < ActiveRecord::Base
has_and_belongs_to_many :assemblies
end
これに対応するマイグレーションは、次のようになります。
class CreateAssembliesAndParts < ActiveRecord::Migration
def change
create_table :assemblies do |t|
t.string :name
t.timestamps
end
create_table :parts do |t|
t.string :part_number
t.timestamps
end
create_table :assemblies_parts do |t|
t.belongs_to :assembly
t.belongs_to :part
end
end
end
2.7 belongs_toとhas_oneのどちらを選択すべきか
もし2つのモデルを1対1の関連性で設定したい場合、
片方にはbelongs_to
を、もう片方にはhas_one
を追加する必要があります。
どのようにして、どちらがどちらになるかを決めればいいのでしょうか?
この識別は、外部キーがどこに配置されているかに依りますが、
(これはbelongs_to
関連付けの宣言クラスのテーブルに対して行われます)
実際のデータの意味付けを考えて指定されるべきです。
has_one
関連付けは、「いずれかの1つがあなたの物です」という意味になり、
そのいずれかの1つは、あなたへの指定を返す事になります。
例えば、「アカウントがサプライヤー(提供者)を所有している」というよりは、
「サプライヤー(提供者)がアカウントを所有している」と言ったほうが、より自然な意味付けと言えます。
この考え方により、下記のような正しい関連付けを作ることが出来ます。
class Supplier < ActiveRecord::Base
has_one :account
end
class Account < ActiveRecord::Base
belongs_to :supplier
end
これに対応するマイグレーションは次のようになります。
class CreateSuppliers < ActiveRecord::Migration
def change
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
end
t.integer :supplier_id
を使用して、外部キーの名前付けを明確に指定しています。
現状のRailsのバージョンでは、代わりにt.references :supplier
を使用することで、
この実装の詳細を抽象化することが出来ます。
2.8 has_many :throughと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
2つ目の多対多の関連付けの宣言方法は、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
関連付けを設定するばよいでしょう。
(ただし、結合テーブルがデータベース内に作られることを覚えておく必要があります)
もし、検証(Validation)やコールバック(callback)、結合モデル上の他の属性が必要なのであれば、
has_many :through
を使用すべきです。
2.9 ポリモーフィック関連付け
関連付けにもう少し踏み込んだ絡み付を行うのが、ポリモーフィック関連付けです。 ポリモーフィック関連付けを使用することで、モデルは1つの関連付けで、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)モデルのインスタンスから、@employee.pictures
とすることで、
写真のコレクションを取得することが出来ます。
同様に、@product.pictures
も取得することが出来ます。
Picture
モデルのインスタンスを持つ場合、
@picture.imageable
を通して、その親を取得することが可能です。
これを動作させるには、ポリモーフィックのインターフェースとして宣言されるモデル内に、
外部キーカラムとtypeカラムの両方を宣言する必要があります。
class CreatePictures < ActiveRecord::Migration
def change
create_table :pictures do |t|
t.string :name
t.integer :imageable_id
t.string :imageable_type
t.timestamps
end
end
end
マイグレーションはt.references
形式を使用することで、これをシンプルにすることが出来ます。
class CreatePictures < ActiveRecord::Migration
def change
create_table :pictures do |t|
t.string :name
t.references :imageable, polymorphic: true
t.timestamps
end
end
end
2.10 自己結合
データモデルを設計する際に、時にモデルは自分自身に関連付けを持たせるべきである、という事に気づく事があります。 例えば、全ての従業員(employees)を1つのデータベースモデルに格納し、 管理者(manager)と部下(subordinates)のような関連付けが追えるようにしたいと考えたとします。 このような場合、自己結合関連付けを使用してモデル化をすることが可能です。
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、トリップ、警告
ここでは、Activeレコード関連付けの知っておくべき幾つかの有効なテクニックについて、説明します。
- キャッシュの制御
- 名前衝突の回避
- スキーマの更新
- 関連付けスコープの制御
- 双方向関連付け
3.1 キャッシュの制御
関連付けのメソッドの全てはキャッシュを中心に構築され、 以降の操作のために直近の有効なクエリーの結果を保持します。 キャッシュはメソッドを跨いで共有されます。 例えば、
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 名前衝突の回避
関連付けのための名付けを、自由に行うことは出来ません。
何故なら、関連付けを作成すると、モデルにその名前を使用したメソッドが追加されるため、
ActiveRecord::Base
のインスタンメソッドとして既に使用されている名前を、
関連付けの名前として与えられると問題が発生するからです。
関連付けのメソッドが基盤となるメソッドを上書きし、破綻してしまいます。
例えば、"attributes"または"connection"は、関連付けには不適切な名前です。
3.3 スキーマの更新
関連付けは非常に便利ではありますが、万能な魔法というわけではありません。
あなたは、あなたの関連付けにマッチするデータベースのスキーマに対して、
継続してメンテナンスをしていく責任があります。
実際には、あなたが作成した関連付けがどのような物なのかに依って2つの事を意味します。
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 change
create_table :orders do |t|
t.datetime :order_date
t.string :order_number
t.integer :customer_id
end
end
end
もし、基板となるモデルを構築した後に関連付けを作成することになった場合は、
必須となる外部キーを提供するadd_column
マイグレーションを作成する必要がある事を覚えておいてください。
3.3.2 has_and_belongs_to_many関連付けのための結合テーブルの作成
has_and_belongs_to_many
を作成する場合、
その結合テーブルを明示的に作成する必要があります。
:join_table
オプションを使用した明示的な結合テーブルの名前の指定が無い限り、
Activeレコードはクラス名の辞書順によって名前を作成します。
そのため、customer(顧客)とorder(注文)モデルの結合は、デフォルトで辞書順により"c"が"o"より優先されて、
"customers_orders"のテーブル名が与えられます。
モデル名間の優先順位は、文字列用に<
演算子を使用して計算されます。
これは、もし文字列の長さが異なり、且つ始まりから最も短い単語(?)部分で同じ文字列が使用されている場合、
長い文字列は辞書順としては短いものより優先順位が高いとみなされます。
例えば、"paper_boxes"のテーブル名と"papers"のテーブルは、
"paper_boxes"の名前の長さから、"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 CreateAssembliesPartsJoinTable < ActiveRecord::Migration
def change
create_table :assemblies_parts, id: false do |t|
t.integer :assembly_id
t.integer :part_id
end
end
end
create_table
にid: false
を渡しているのは、テーブルがモデルを表さないからです。
これは適切に関連付けが動作するために必要なことです。
もし、has_and_belongs_to_many
関連付けの中で、
モデルIDの破損、ID衝突による例外といった不審な挙動を見かける場合は、
あなたがこの事を忘れている可能性があります。
3.4 関連付けスコープの制御
デフォルトでは、関連付けはオブジェクトを現在のモジュールのスコープ内でしか探しません。 これはActiveレコードモデルをモジュール内に定義する際に、重要になる可能性があります。 例えば、
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
3.5 双方向の関連付け
関連付けが双方向に動作するのは通常のことで、 異なる2つのモデルそれぞれに定義が必要になります。
class Customer < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :customer
end
デフォルトでは、Activeレコードはこれらの関連付けの間の繋がりを把握していません。 これは、2つのオブジェクトの複製を作ることで、同期が外れることを知ることが出来ます。
c = Customer.first
o = c.orders.first
c.first_name == o.customer.first_name # => true
c.first_name = 'Manny'
c.first_name == o.customer.first_name # => false
これはcとo.customerは同じ値のデータですが、異なるメモリ上に置かれ、
片方が変更されることで自動的にもう片方も更新されるという事が無いためです。
Activeレコードは:inverse_of
オプションを提供することで、
これらの関係への通知を可能にしてくれます。
class Customer < ActiveRecord::Base
has_many :orders, inverse_of: :customer
end
class Order < ActiveRecord::Base
belongs_to :customer, inverse_of: :orders
end
この変更により、Activeレコードはcutomerオブジェクトの服せうを1つだけ読み込むことでデータの矛盾を防ぎ、 アプリケーションがより効率的になります。
c = Customer.first
o = c.orders.first
c.first_name == o.customer.first_name # => true
c.first_name = 'Manny'
c.first_name == o.customer.first_name # => true
inverse_of
のサポートが制限されるケースが幾つか存在します。
-
:through
の関連付けでは動作しません。 -
:polymorphic
の関連付けでは動作しません。 -
:as
の関連付けでは動作しません。 -
belongs_to
へのhas_many
のinverse_ofは無視されます。(翻訳に自信なし)
3. チップス、テクニック、注意点
以降のセクションでは、追加されるメソッドと関連付けを定義する際に使用することが出来るオプションを含む、 各関連付けの詳細について説明していきます。
4.1 belongs_to関連付けについて
belongs_to
は、他のモデルとの1対1の関連付けを作ります。
データベースの観点から言えば、この関連付けは、このクラスが外部キーを含むという事になります。
もし、そのクラスでは無い別のクラスが外部キーを含む場合は、
belongs_to
の代わりにhas_one
を使用するべきです。
4.1.1 belongs_toによって追加されるメソッド
belongs_to
関連付けを定義すると、
定義されたクラスには、関連付けに関する自動的に4つのメソッドが与えられます。
association(force_reload = false)
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
これらのメソッドの全ては、belongs_to
の第1引数として渡されたシンボルの関連付けを置き換えます。
例えば、次のように定義された場合、
class Order < ActiveRecord::Base
belongs_to :customer
end
各order(注文)モデルのインスタンスは、下記のメソッドを持つ事になります。
customer
customer=
build_customer
create_customer
新しいhas_one
またはbelongs_to
の関連付けを初期化する際には、
has_many
またはhas_and_belongs_to_many
に使用されるassociation.build
メソッドでは無く、
build_
接頭辞を関連付けのbuildに使用しなければいけません。
createであれば、create_
接頭辞を使用します。
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
メソッドは、関連付する型の新しいオブジェクトを返します。
このオブジェクトは引数として渡された属性(attributes)からインスタンス化され、
このオブジェクトの外部キーを通してリンクが設定され、
関連付けのモデル上で指定された全ての検証をパスすると、関連付けられたオブジェクトは保存されます。
@customer = @order.create_customer(customer_number: 123,
customer_name: "John Doe")
4.1.2 belongs_toのオプション
Railsが、ほとんどの状況に適した気の利いたデフォルト設定を使用してくれる一方、
belongs_to
関連付け参照の振る舞いを、カスタマイズしたいという事があるかもしれません。
そのようなカスタマイズは、関連付けを作成する際に、オプションとスコープブロックを渡すことで簡単に行うことが可能です。
例えば、この関連付けは、そのような2つのオプションを使用します。
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
4.1.2.1 :autosave
:autosave
オプションをtrue
に設定すると、
親のオブジェクトを保存する度に、読み込まれたメンバーを保存し、
削除するものとしてマークされたメンバーを削除します。
4.1.2.2 :class_name
他のモデルの名前が、関連付くものの名前から引き出すことが出来ない場合、
:class_name
オプションを使用して、モデル名を抵抗することが可能です。
例えば、order(注文)がcustomer(顧客)に属しているが、
customer(顧客)が含まれる実際のモデルの名前がPatron
である場合、
次のように設定することが出来ます。
class Order < ActiveRecord::Base
belongs_to :customer, class_name: "Patron"
end
4.1.2.3 :counter_cache
:counter_cache
(カウンターキャッシュ)オプションは、より効率的に従属するオブジェクトの数を調べるために使用されます。
下記の2つのモデルがあると想定します。
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.4 :dependent
:destroy
に:dependent
オプションを設定すると、
このオブジェクトの削除の際に、関連付けられたオブジェクト上でそのオブジェクトを削除するためのdestroy
メソッドが呼び出されます。
もし:delete
に:dependent
オプションを設定すると、
このオブジェクトの削除の際に、destroy
メソッドの呼び出し無しで、関連付けられたオブジェクトは削除されます。
もし:restrict
に:dependent
オプションを設定すると、
このオブジェクトを削除しようとした際に、もし関連付けられたオブジェクトが存在すると、
ActiveRecord::DeleteRestrictionError
が発生します。
このオプションを他のクラスへのhas_many
に繋がる、
belongs_to
の関連付け上で指定すべきではありません。
これはデータベース内で、いわゆる親が居ないレコードを存在させてしまう事に繋がります。
4.1.2.5 :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.6 :inverse_of
:inverse_of
オプションは、この関連付けを逆さまにする、
has_many
またはhas_one
関連付けの名前を指定します。
:polymorphic
オプションと組み合わせて、動作させることは出来ません。
class Customer < ActiveRecord::Base
has_many :orders, inverse_of: :customer
end
class Order < ActiveRecord::Base
belongs_to :customer, inverse_of: :orders
end
4.1.2.7 :polymorphic
:polymorphic
オプションへtrue
を渡すと、それはポリモーフィック関連付けであることを指し示す事になります。
ポリモーフィック関連付けについては、既にこのガイド内で詳細な説明がされています。
4.1.2.8 :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.9 :validate
もし:validate
オプションをtrue
に設定すると、
関連付けられたオブジェクトは、このオブジェクトが保存される度に検証されることになります。
デフォルトではfalse:
で、関連付けられたオブジェクトは、このオブジェクトが保存される際に検証されません。
4.1.3 belongs_toのスコープ
belongs_to
を使用する際に、クエリーをカスタマイズしたいというケースがあるかもしれません。
そのようなカスタマイズは、スコープのブロックを通して行うことが可能です。
例えば、
class Order < ActiveRecord::Base
belongs_to :customer, -> { where active: true },
dependent: :destroy
end
スコープブロック内では、標準のクエリーメソッド を幾つか使用することが可能です。 該当するものを下記に示します。
where
includes
readonly
select
4.1.3.1 where
where
メソッドは、関連付くオブジェクトが満たすべき条件を指定することを可能にしてくれます。
class Order < ActiveRecord::Base
belongs_to :customer, -> { where active: true }
end
4.1.3.2 includes
includes
メソッドを使用して、2番目として要求されるeager-load(熱心な読み込み)されるべき、
関連付けを指定する事が可能です。
例として、次のモデルがあるとします。
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
として、
しきりにcustomers(顧客)を直接取得するのであれば、
customersをline itemsのorderへ含めることで、コードをより効率的にすることが出来ます。
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
直に接している関連付けのためにincludes
を使用する必要はありません。
もし、Order belongs_to :customer
が指定されていれば、
customerはそれが必要な時に、自動的にeager-loadされます。
4.1.3.3 readonly
readonly
を使用すると、関連付けを通して取得された関連オブジェクトは読み込み専用になります。
4.1.3.4 select
select
メソッドは、関連付けオブジェクトのデータ取得時に使用するSQLのSELECT句の上書きをさせてくれます。
デフォルトでは、Railsは全てのカラムを取得します。
もしselect
メソッドをbelongs_to
関連付けの上で使用するのであれば、
:foreign_key
オプションも正しい結果を得るための保険として設定しておくべきです。
4.1.4 関連付けオブジェクトは存在するか?
association.nil?
メソッドを使用することで、関連付けオブジェクトが存在するかを確認することが出来ます。
if @order.customer.nil?
@msg = "No customer found for this order"
end
4.1.5 オブジェクトはいつ保存されるか?
オブジェクトへのbelongs_to
関連付けの割り当ては、
自動的にオブジェクトの保存を行いません。
同様に関連付けられたオブジェクトも、保存を行いません。
4.2 has_one関連付けについて
has_one
関連付けは、他のモデルとの1対1の関連付けを作ります。
データベースの観点から言えば、この関連付けは、他のクラスが外部キーを含むとも言えます。
もし、このクラスが外部キーを含む場合は、
has_one
の代わりにbelongs_to
を使用すべきです。
4.2.1 has_oneによって追加されるメソッド
has_one
関連付けを定義すると、
定義されたクラスには、関連付けに関する4つのメソッドが自動的に与えられます。
association(force_reload = false)
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
これらのメソッドの全ては、has_one
の第1引数として渡されたシンボルの関連付けを置き換えます。
例えば、次のように定義された場合、
class Supplier < ActiveRecord::Base
has_one :account
end
各Supplier
(提供者)モデルのインスタンスは、下記のメソッドを持つ事になります。
account
account=
build_account
create_account
新しいhas_one
またはbelongs_to
の関連付けを初期化する際には、
has_many
またはhas_and_belongs_to_many
に使用されるassociation.build
メソッドでは無く、
build_
接頭辞を関連付けのbuildに使用しなければいけません。
createであれば、create_
接頭辞を使用します。
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
メソッドは、関連付する型の新しいオブジェクトを返します。
このオブジェクトは引数として渡された属性(attributes)からインスタンス化が行われ、
リンクはその外部キーを通して設定され、
関連付けのモデル上で指定された全ての検証をパスすると、関連付けられたオブジェクトは保存されます。
@account = @supplier.create_account(terms: "Net 30")
4.2.2 has_oneのオプション
Railsが、ほとんどの状況に適した気の利いたデフォルト設定を使用してくれる一方、
has_one
関連付け参照の振る舞いを、カスタマイズしたいという事があるかもしれません。
そのようなカスタマイズは、関連付けを作成する際に、オプションを渡すことで簡単に行うことが可能です。
例えば、この関連付けは、そのような2つのオプションを使用します。
class Supplier < ActiveRecord::Base
has_one :account, class_name: "Billing", dependent: :nullify
end
has_one
関連付けは、下記のオプションをサポートします。
:as
:autosave
:class_name
:dependent
:foreign_key
:inverse_of
:primary_key
:source
:source_type
:through
:validate
4.2.2.1 :as
:as
オプションの設定は、ポリモーフィズムの関連付けであることを指し示します。
ポリモーフィック関連付けについては、既にこのガイド内で詳細な説明がされています。
4.2.2.2 :autosave
:autosave
オプションをtrue
に設定すると、
親のオブジェクトを保存する度に、読み込まれたメンバーを保存し、
削除するものとしてマークされたメンバーを削除します。
4.2.2.3 :class_name
他のモデルの名前が、関連付くものの名前から引き出すことが出来ない場合、
:class_name
オプションを使用して、モデル名を抵抗することが可能です。
例えば、supplier(提供者)がaccount(アカウント)を持つが、
accounts(アカウント)が含まれる実際のモデルの名前がBilling
(請求書)である場合、
次のように設定することが出来ます。
class Supplier < ActiveRecord::Base
has_one :account, class_name: "Billing"
end
4.2.2.4 :dependent
オーナー(持ち主)が削除された際の、関連付けられたオブジェクトの制御を行います。
-
:destroy
を指定されると、関連付けられたオブジェクトも削除します。 -
:delete
が指定されると、データベースから直接、関連付けられたオブジェクトが削除されます。 (そのため、コールバックが実行されません。) -
:nullify
が指定されると、外部キーにNULL
が設定されます。 コールバックは実行されません。 -
:restrict_with_exception
が指定されると、関連付けられたレコードが存在する場合、例外が発生します。 -
:restrict_with_error
が指定されると、関連付けられたオブジェクトが存在する場合、オーナー(持ち主)に対してエラーが追加されます。
4.2.2.5 :foreign_key
便利な事にRailsは、もう1つのモデル上で、このモデルの名前に_id
接頭辞を追加したカラムを、
外部キーを保持するためのカラムとみなしてくれます。
:foreign_key
オプションは、直接外部キーの名前を設定することを可能にしてくれます。
class Supplier < ActiveRecord::Base
has_one :account, foreign_key: "supp_id"
end
あるケースでは、Railsは外部キーのカラムを作成しないことがあります。 その場合は、マイグレーションの一部で明示的に定義する必要があります。
4.2.2.6 :inverse_of
:inverse_of
オプションは、この関連付けを逆さまにする、
belongs_to
関連付けの名前を指定します。
:through
または:as
オプションと組み合わせて動作させることは出来ません。
class Supplier < ActiveRecord::Base
has_one :account, inverse_of: :supplier
end
class Account < ActiveRecord::Base
belongs_to :supplier, inverse_of: :account
end
4.2.2.7 :primary_key
便利な事に、Railsはこのモデルのid
を、主キーを保持するのに使用するカラムとみなしてくれます。
:primary_key
オプションを使用して、これを上書きし、明示的に主キーを指定することが可能です。
4.2.2.8 :source
:source
オプションは、has_one :through
関連付けのために、
sourceの関連付け名を指定します。
4.2.2.9 :source_type
:source_type
オプションは、ポリモーフィック関連付けを通して処理されるhas_one :through
関連付けのために、
source関連付けの型を指定します。(翻訳に自信なし)
4.2.2.10 :through
:through
オプションは、クエリーの実行を通す結合テーブルを指定します。
has_one :through
関連付けは、既にこのガイド内で詳細な説明がされています。
4.2.2.11 :validate
もし:validate
オプションをtrue
に設定すると、
関連付けられたオブジェクトは、このオブジェクトが保存される度に検証されることになります。
デフォルトではfalse:
で、関連付けられたオブジェクトは、このオブジェクトが保存される際に検証されません。
4.2.3 has_oneのスコープ
has_one
を使用する際に、クエリーをカスタマイズしたいというケースがあるかもしれません。
そのようなカスタマイズは、スコープのブロックを通して行うことが可能です。
例えば、
class Supplier < ActiveRecord::Base
has_one :account, -> { where active: true }
end
スコープブロック内では、標準のクエリーメソッドを幾つか使用することが可能です。 該当するものを下記に示します。
where
includes
readonly
select
4.2.3.1 where
where
メソッドは、関連付くオブジェクトが満たすべき条件を指定することを可能にしてくれます。
class Supplier < ActiveRecord::Base
has_one :account, -> { where "confirmed = 1" }
end
4.2.3.2 includes
この関連付けが使用される際に、テーブルの繋がりが2階層目として要求されるeager-load(熱心な読み込み)されるべきテーブルを、
includes
メソッドを使用することで、その関連付けを指定する事が可能です。
例として、次のモデルがあるとします。
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
もし、suppliers(提供者)から、@supplier.account.representative
として、
しきりにrepresentatives(代理人)を直接取得するのであれば、
representativesをsuppliersのaccountsに含めることで、コードをより効率的にすることが出来ます。
class Supplier < ActiveRecord::Base
has_one :account, -> { includes :representative }
end
class Account < ActiveRecord::Base
belongs_to :supplier
belongs_to :representative
end
class Representative < ActiveRecord::Base
has_many :accounts
end
4.2.3.3 readonly
readonly
メソッドを使用すると、関連付けを通して取得された関連オブジェクトは読み込み専用になります。
4.2.3.4 select
select
メソッドは、関連付けオブジェクトのデータ取得時に使用するSQLのSELECT句の上書きをさせてくれます。
デフォルトでは、Railsは全てのカラムを取得します。
4.2.4 関連付けオブジェクトは存在するか?
association.nil?
メソッドを使用することで、関連付けオブジェクトが存在するかを確認することが出来ます。
if @supplier.account.nil?
@msg = "No account found for this supplier"
end
4.2.5 オブジェクトはいつ保存されるか?
has_one
関連付けにオブジェクトに割り当てる際に、
オブジェクトは自動的に保存されます(それの外部キーを更新するために)。
また、その外部キーも変更されるため、置き換えられるオブジェクトも自動的に保存されます。
これらのいずれかの保存が検証エラーによって失敗した場合、割り当て(代入)文はfalse
を返し、
割り当て自体はキャンセルされます。
もし、オブジェクトを保存すること無く、has_one
関連付けにオブジェクトを割り当てたいのであれば、
association.build
メソッドを使用してください。
4.3 has_many関連付けについて
has_many
関連付けは、他のモデルに対して1対多の関係性を構築します。
データベースの観点からはこの関連付けは、他のクラスがこのクラスのインスタンスを参照する外部キーを持つと言えます。
4.3.1 has_manyによって追加されるメソッド
has_many
関連付けを定義すると、
定義されたクラスには自動的に、関連付けに関係する13個のメソッドが自動的に与えられます。
collection(force_reload = false)
collection<<(object, ...)
collection.delete(object, ...)
collection.destroy(object, ...)
collection=objects
collection_singular_ids
collection_singular_ids=ids
collection.clear
collection.empty?
collection.size
collection.find(...)
collection.where(...)
collection.exists?(...)
collection.build(attributes = {}, ...)
collection.create(attributes = {})
これらのメソッドの全ては、collection
の部分はhas_many
に1つ目の引数として渡されたシンボルと置き換えられ、
collection_singular
の部分は、そのシンボルの単数形と置き換えられます。
例えば、下記のように宣言された場合、
class Customer < ActiveRecord::Base
has_many :orders
end
各顧客(customer)モデルのインスタンスは、下記のメソッドを持つ事になります。
orders(force_reload = false)
orders<<(object, ...)
orders.delete(object, ...)
orders.destroy(object, ...)
orders=objects
order_ids
order_ids=ids
orders.clear
orders.empty?
orders.size
orders.find(...)
orders.where(...)
orders.exists?(...)
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<<
メソッドは、1つまたは複数のオブジェクトを、
それらの外部キーを呼び出したモデルの主キーを設定することでcollectionに加えます。
@customer.orders << @order1
4.3.1.3 collection.delete(object, ...)
collection.delete
メソッドは、外部キーにNULL
を設定することで、
collectionから1つまたは複数のオブジェクトを削除します。
@customer.orders.delete(@order1)
加えて、オブジェクトがdependent: :destroy
に関連付けられているとdestroyされ、
¥dependent: :delete_all
に関連付けられているとdeleteされます。
4.3.1.4 collection.destroy(object, ...)
collection.destroy
メソッドは、各オブジェクト上でdestroy
することで、
collectionから1つまたは複数のオブジェクトを削除します。
@customer.orders.destroy(@order1)
オブジェクトは常にデータベースから削除され、:dependent
オプションは無視されます。
4.3.1.5 collection=objects
collection=
メソッドは、適切な追加と削除を行うことで、
collectionに提供されたオブジェクト(objects)のみを含めるようにします。
4.3.1.6 collection_singular_ids
collection_singular_ids
メソッドは、collection内のオブジェクトのidの配列を返します。
@order_ids = @customer.order_ids
4.3.1.7 collection_singular_ids=ids
collection_singular_ids=
メソッドは、適切に追加と削除を行うことによって、
提供された主キーの値のIDのオブジェクトだけをcollectionに含めるようにします。
4.3.1.8 collection.clear
collection.clear
メソッドは、collectionから全てのオブジェクトを削除します。
dependent: :destroy
が関連付けられていれば、関連するオブジェクトをdestroyし、
dependent: :delete_all
が関連付けられていれば、データベースから直接deleteし、
このいずれかで無ければ、それらの外部キーにNULL
を設定します。
4.3.1.9 collection.empty?
collection.empty?
メソッドは、collectionが関連付けられたオブジェクトを1つも含まない場合にtrue
を返します。
<% if @customer.orders.empty? %>
No Orders Found
<% end %>
4.3.1.10 collection.size
collection.size
メソッドは、コレクション内のオブジェクトの数を返します。
@order_count = @customer.orders.size
4.3.1.11 collection.find(...)
collection.find
メソッドは、コレクション内のオブジェクトを探します。
ActiveRecord::Base.find
と同じ文法とオプションを使用します。
@open_orders = @customer.orders.find(1)
4.3.1.12 collection.where(...)
collection.where
メソッドは、与えられた条件に基づいて、コレクション内からオブジェクトを探しますが、
オブジェクトの読み込みは遅くなります。
これは、データベースがオブジェクトがアクセスされる時にのみ、データベースにクエリーが発行されるためです。
@open_orders = @customer.orders.where(open: true) # まだクエリーは未発行
@open_order = @open_orders.first # ここでデータベースにクエリーを発行
4.3.1.13 collection.exists?(...)
collection.exists?
メソッドは、コレクション内に与えられた条件に合うオブジェクトが存在するか否かを調べます。
ActiveRecord::Base.exists?
と同じ文法とオプションを使用します。
4.3.1.14 collection.build(attributes = {}, ...)
collection.build
メソッドは、1つ以上の関連付けられた型の、新しいオブジェクトを返します。
これらのオブジェクトは、渡された属性からインスタンス化され、
外部キーが作成されることでリンクされますが、これらの関連付けられたオブジェクトはまだ保存されません。
@order = @customer.orders.build(order_date: Time.now,
order_number: "A12345")
4.3.1.15 collection.create(attributes = {})
collection.create
は、関連付けられた型の新しいオブジェクトを返します。
このオブジェクトは、渡された属性からインスタンス化され、
その外部キーが作成されることでリンクされ、
関連付けられたモデル上で指定されている検証に全てパスすれば、そのオブジェクトは保存されます。
@order = @customer.orders.create(order_date: Time.now,
order_number: "A12345")
4.3.2 has_manyのオプション
Railsが、ほとんどの状況に適した気の利いたデフォルト設定を使用してくれる一方、
has_many
関連付け参照の振る舞いを、カスタマイズしたいという事があるかもしれません。
そのようなカスタマイズは、関連付けを作成する際に、オプションを渡すことで簡単に行うことが可能です。
例えば、この関連付けは、そのような2つのオプションを使用します。
class Customer < ActiveRecord::Base
has_many :orders, dependent: :delete_all, validate: :false
end
has_many
関連付けは、下記のオプションをサポートします。
:as
:autosave
:class_name
:dependent
:foreign_key
:inverse_of
:primary_key
:source
:source_type
:through
:validate
4.3.2.1 :as
:as
オプションの設定は、既にこのガイド内で説明された、ポリモーフィック関連付けを指定します。
4.3.2.2 :autosave
:autosave
オプションをtrue
に設定すると、
親のオブジェクトを保存する度に、読み込まれたメンバーを保存し、
削除するものとしてマークされたメンバーを削除します。
4.3.2.3 :class_name
他のモデルの名前が、関連付くものの名前から引き出すことが出来ない場合、
:class_name
オプションを使用して、モデル名を抵抗することが可能です。
例えば、customer(顧客)がorders(注文)を持つが、
orders(アカウント)が含まれる実際のモデルの名前がTransaction
(処理)である場合、
次のように設定することが出来ます。
class Customer < ActiveRecord::Base
has_many :orders, class_name: "Transaction"
end
4.3.2.4 :dependent
関連付けられたオブジェクトのオーナー(持ち主)がdestroyされた際に、何を発生させるかを制御します。
-
:destroy
が指定されると、全ての関連付けられた全てのオブジェクトも削除します。 -
:delete_all
が指定されると、関連付けられた全てのオブジェクトはデータベースから直接削除されます。 (そのため、コールバックは実行されません。) -
:nullify
が指定されると、外部キーにNULL
が設定されます。 コールバックは実行されません。 -
:restrict_with_exception
が指定されると、関連付けられたレコードが存在すると、例外が発生します。 -
:restrict_with_error
が指定されると、関連付けられたオブジェクトが存在する場合、オーナー(持ち主)に対してエラーが追加します。
関連付けに:through
が使用されていると、このオプションは無視されます。
4.3.2.5 :foreign_key
便利な事にRailsは、もう1つのモデル上で、このモデルの名前に_id
接頭辞を追加したカラムを、
外部キーを保持するためのカラムとみなしてくれます。
:foreign_key
オプションは、直接外部キーの名前を設定することを可能にしてくれます。
class Customer < ActiveRecord::Base
has_many :orders, foreign_key: "cust_id"
end
あるケースでは、Railsは外部キーカラムを作成しません。 マイグレーションの一部として、明示的に定義する必要があります。
4.3.2.6 :inverse_of
:inverse_of
オプションは、この関連付けを逆さまにする、
belongs_to
関連付けの名前を指定します。
:through
または:as
オプションと組み合わせて動作させることは出来ません。
class Customer < ActiveRecord::Base
has_many :orders, inverse_of: :customer
end
class Order < ActiveRecord::Base
belongs_to :customer, inverse_of: :orders
end
4.3.2.7 :primary_key
便利な事に、Railsはこの関連付けのid
を、主キーを保持するのに使用するカラムとみなしてくれます。
:primary_key
オプションを使用して、これを上書きし、明示的に主キーを指定することが可能です。
4.3.2.8 :source
:source
オプションは、has_many :through
関連付けのために、
sourceの関連付け名を指定します。
関連付けの名前から、source関連付けの名前が自動的に推測されて指定されない場合にのみ、このオプションを使用する必要があります。
4.3.2.9 :source_type
:source_type
オプションは、ポリモーフィック関連付けを通して処理されるhas_many :through
関連付けのために、
source関連付けの型を指定します。(翻訳に自信なし)
4.3.2.10 :through
:through
オプションは、クエリーの実行を通す結合テーブルを指定します。
The :through option specifies a join model through which to perform the query.
has_many :through
関連付けが提供する多対多の実装は、既にこのガイド内で詳細な説明がされています。
4.3.2.11 :validate
もし:validate
オプションにfalse
が設定されると、
関連付けられたオブジェクトは、そのオブジェクトが保存される際に検証がされなくなります。
デフォルトではこの値はtrue
で、そのオブジェクトが保存される際は検証が行われます。
4.3.3 has_manyのスコープ
has_many
を使用する際に、クエリーをカスタマイズしたいというケースがあるかもしれません。
そのようなカスタマイズは、スコープのブロックを通して行うことが可能です。
例えば、
class Customer < ActiveRecord::Base
has_many :orders, -> { where processed: true }
end
スコープブロック内では、標準のクエリーメソッドを幾つか使用することが可能です。 該当するものを下記に示します。
where
extending
group
includes
limit
offset
order
readonly
select
uniq
4.3.3.1 where
where
メソッドは、関連付くオブジェクトが満たすべき条件を指定することを可能にしてくれます。
class Customer < ActiveRecord::Base
has_many :confirmed_orders, -> { where "confirmed = 1" },
class_name: "Order"
end
またハッシュを通じて、条件を設定する事も可能です。
class Customer < ActiveRecord::Base
has_many :confirmed_orders, -> { where confirmed: true },
class_name: "Order"
end
もしハッシュスタイルのwhere
オプションを使用する場合、
この関連付けを通したれコード作成は、ハッシュを使用して自動的にスコープ化されます。
このケースでは、@customer.confirmed_orders.create
、または、@customer.confirmed_orders.build
を使用すると、
confirmedカラムの値がtrue
であるordersを作成します。
4.3.3.2 extending
extending
メソッドは、関連プロキシを拡張する名付けモジュールを指定します。
関連付けの拡張の詳細については、後ほどこのガイド内で説明します。
4.3.3.3 group
group
メソッドは、結果的にSQLでGROUP BY
の対象となる、
属性名を指定するのに使用されます。
class Customer < ActiveRecord::Base
has_many :line_items, -> { group 'orders.id' },
through: :orders
end
4.3.3.4 includes
この関連付けが使用される際に、テーブルの繋がりが2階層目として要求されるeager-load(熱心な読み込み)されるべきテーブルを、
includes
メソッドを使用することで、その関連付けを指定する事が可能です。
例えば、下記のようなモデルがあったとします。
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
もし、customers(顧客)から、@customer.orders.line_items
として、
しきりにline items(品目)を直接取得するのであれば、
line itemsをcustomersのordersに含めることで、コードをより効率的にすることが出来ます。
class Customer < ActiveRecord::Base
has_many :orders, -> { includes :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.3.5 limit
limit
メソッドは、関連付けを通して取得されるオブジェクトの総数を制限してくれます。
class Customer < ActiveRecord::Base
has_many :recent_orders,
-> { order('order_date desc').limit(100) },
class_name: "Order",
end
4.3.3.6 offset
offset
メソッドは、関連付けを通じて取得するオブジェクトの開始(オフセット)位置の指定を可能にしてくれます。
例えば、-> { offset(11) }
は最初の11レコードをスキップします。
4.3.3.7 order
order
メソッドは、関連付けられて取得するオブジェクトの順序を指定します。
(ORDER BY
句で使用されるSQL文)
class Customer < ActiveRecord::Base
has_many :orders, -> { order "date_confirmed DESC" }
end
4.3.3.8 readonly
readonly
メソッドを使用すると、
関連付けを通じて取得する際に、関連付けられたオブジェクトは読み込み専用になります。
4.3.3.9 select
select
メソッドは、関連付けられたオブジェクトを取得する際の、SQL文のSELECT
句の上書きを可能にしてくれます。
デフォルトでは、Railsは全てのカラムを取得します。
もし自分でselect
を指定するのであれば、関連付けられたモデルの主キーと外部キーのカラムが、含まれているかを確認してください。
これを行わないと、Railsはエラーをスローします。
4.3.3.10 distinct
重複の無いコレクションを保持するために、distinct
メソッドを使用します。
これは: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>]
上記のケースでは、2つのreadingsが存在し、
person.posts
は例えばそれらが同じpostを指し示していても、
両方を取り出してしまいます。
それでは、distinct
を設定してみましょう。
class Person
has_many :readings
has_many :posts, -> { distinct }, through: :readings
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>]
上記のケースも、2つのreadingsが存在します。
ただし、person.posts
は、コレクションが一意のレコードしか読み込まないため、
1つのpostのみを表示します。
もしレコードの挿入時に、指定されている関連付け内で、全てのレコードをdistinct(重複無し)にしたいのであれば
(そのため、関連付けを調べる際に重複レコードが見つからなくなります。)、
自身でテーブルに一意(unique)のインデックス(index)を追加するべきです。
例えば、もしテーブル名をperson_posts
とし、全てのpostを一意にしたいのであれば、
下記のマイグレーションを追加します。
add_index :person_posts, :post, :unique => true
一意であることを確約する事にinclude?
を使用するような方法を取ってしまうと、
レースコンディションになることに注意してください。
関連付けで、distinct(重複が無いこと)を確約するのに、include?
を使用しないでください。
例えば、上述でのpostでは下記のコードは、
複数のユーザーが同時にこの処理の実行を試みる可能性があるため、
レースコンデションになります。
(一意を確約しているはずが、複数ユーザーによる同時追加が発生すると、一意制約が破られてしまう可能性があります。)
person.posts << post unless person.posts.include?(post)
4.3.4 オブジェクトはいつ保存されるか?
has_many
関連付けへのオブジェクトの割り当てをする際に、そのオブジェクトは自動的に保存されます。
(そのオブジェクト自身の外部キーを更新することで)
もし複数のオブジェクトを1文の中で割り当てたのであれば、それらの全てが保存されます。
もし、それらの内のいずれかのオブジェクトが、検証エラーのため保存に失敗したのであれば、
その割り当て文はfalse
を返し、その割り当て処理自身がキャンセルされます。
もし親となるオブジェクト(has_many
宣言を宣言しているオブジェクト)が保存されない(new_record?
がtrue
を返すという事)と、
子オブジェクトは追加された際に保存されません。
全ての保存されていない関連付けの項目は、親が保存された際に自動的に保存されます。
オブジェクトの保存をせずに、has_many
関連付けにオブジェクトを割り当てたい場合は、
collection.build
メソッドを使用してください。
4.4 has_and_belongs_to_many関連付けに付いて
has_and_belongs_to_many
関連付けは、他のオブジェクトとの多対多の関連付けを作ります。
データベースの観点から言うと、それぞれのクラスを参照する外部キーを含む中間結合テーブルが、
この2つのクラスを関連付けます。
4.4.1 has_and_belongs_to_manyによって追加されるメソッド
has_and_belongs_to_many
関連付けを宣言すると、
宣言したクラスには自動的に関連付けに関係する13個のメソッドが与えられます。
collection(force_reload = false)
collection<<(object, ...)
collection.delete(object, ...)
collection.destroy(object, ...)
collection=objects
collection_singular_ids
collection_singular_ids=ids
collection.clear
collection.empty?
collection.size
collection.find(...)
collection.where(...)
collection.exists?(...)
collection.build(attributes = {})
collection.create(attributes = {})
これらのメソッドの全ては、collection
の部分はhas_and_belongs_to_many
に1つ目の引数として渡されたシンボルと置き換えられ、
collection_singular
の部分は、そのシンボルの単数形と置き換えられます。
例えば、下記のように宣言された場合、
class Part < ActiveRecord::Base
has_and_belongs_to_many :assemblies
end
モデル部分の各インスタンスは、これらのメソッドを持つことになります。
assemblies(force_reload = false)
assemblies<<(object, ...)
assemblies.delete(object, ...)
assemblies.destroy(object, ...)
assemblies=objects
assembly_ids
assembly_ids=ids
assemblies.clear
assemblies.empty?
assemblies.size
assemblies.find(...)
assemblies.where(...)
assemblies.exists?(...)
assemblies.build(attributes = {}, ...)
assemblies.create(attributes = {})
4.4.1.1 追加カラムのメソッド
もし、has_and_belongs_to_many
関連付けの結合テーブルが、2つの外部キーの他に追加カラムを持つ場合、
これらのカラムは関連付けを通して取得されるレコードへの属性として追加されます。
追加属性を使用して返されたレコードは、Railsはそれらの属性を変更して保存することは出来ないため、
常に読み込み専用になります。
has_and_belongs_to_many
関連付け内の結合テーブル上で、追加属性を使用することは非推奨となっています。
もし多対多の関連付けによる2つのモデルの結合テーブル上で、この種の複雑な振る舞いが必要なのであれば、
代わりにhas_many :through
の関連付けを使用すべきです。
4.4.1.2 collection(force_reload = false)
collection
メソッドは、関連付けられたオブジェクトの全ての配列を返します。
もし関連付けられたオブジェクトが無ければ、空の配列が返されます。
@assemblies = @part.assemblies
4.4.1.3 collection<<(object, ...)
collection<<
メソッドは、結合テーブル内にレコードを作成することで、
1つ以上のオブジェクトをコレクションに追加します。
@part.assemblies << @assembly1
このメソッドは、collection.concat
とcollection.push
としてエイリアスされます。
4.4.1.4 collection.delete(object, ...)
collection.delete
メソッドは、結合テーブル内からレコードを削除することで、
コレクションから1つ以上のオブジェクトを削除します。
これは、オブジェクトを削除しません。
@part.assemblies.delete(@assembly1)
結合テーブル上のコールバックをトリガしません。
4.4.1.5 collection.destroy(object, ...)
collection.destroy
メソッドは、コールバック実行を含む結合テーブル内の各レコードの削除を実行することで
コレクションから1つ以上のオブジェクトを削除します。
これはオブジェクトを削除しません。
@part.assemblies.destroy(@assembly1)
4.4.1.6 collection=objects
collection=
メソッドは、適切に追加と削除を行うことによって、
提供されたオブジェクトだけを含むコレクションを作成します。
4.4.1.7 collection_singular_ids
collection_singular_ids
メソッドは、コレクション内のオブジェクトのIDの配列を返します。
@assembly_ids = @part.assembly_ids
4.4.1.8 collection_singular_ids=ids
collection_singular_ids=
メソッドは、適切に追加と削除を行うことによって、
提供された主キーの値によって識別されたオブジェクトだけを含むコレクションを作成します。
4.4.1.9 collection.clear
collection.clear
メソッドは、結合テーブルから行を削除することによって、
各オブジェクトを削除します。
これは関連付けられたオブジェクトを削除しません。
4.4.1.10 collection.empty?
もしコレクションが関連付けられたオブジェクトを含まない場合、
collection.empty?
メソッドはtrue
を返します。
<% if @part.assemblies.empty? %>
This part is not used in any assemblies
<% end %>
4.4.1.11 collection.size
collection.size
メソッドは、コレクション内のオブジェクト数を返します。
@assembly_count = @part.assemblies.size
4.4.1.12 collection.find(...)
collection.find
メソッドは、コレクション内のオブジェクトを見つけます。
ActiveRecord::Base.find
と同じ文法とオプションを使用します。
また、コレクション内にオブジェクトが存在するべき付加条件の追加も行います。(翻訳に自信なし)
@assembly = @part.assemblies.find(1)
4.4.1.13 collection.where(...)
collection.where
メソッドは、提供された条件を元にコレクション内からオブジェクトを見つけますが、
オブジェクトの読み込みは遅くなります。
これは、データベースがオブジェクトがアクセスされる時にのみ、データベースにクエリーが発行されるためです。
また、コレクション内にオブジェクトが存在するべき付加条件の追加も行います。(翻訳に自信なし)
@new_assemblies = @part.assemblies.where("created_at > ?", 2.days.ago)
4.4.1.14 collection.exists?(...)
collection.exists?
メソッドは、
collection.exists?
メソッドは、コレクション内に与えられた条件に合うオブジェクトが存在するか否かを調べます。
ActiveRecord::Base.exists?
と同じ文法とオプションを使用します。
4.4.1.15 collection.build(attributes = {})
collection.build
メソッドは、関連付けられた型の新しいオブジェクトを返します。
このオブジェクトは、渡された属性からインスタンス化され、
結合テーブルを通してリンクが作成されますが、
関連付けられたこのオブジェクトは、まだ保存されません。
@assembly = @part.assemblies.build({assembly_name: "Transmission housing"})
4.4.1.16 collection.create(attributes = {})
collection.create
メソッドは、関連付けられた型の新しいオブジェクトを返します。
このオブジェクトは、渡された属性からインスタンス化され、
結合テーブルを通してリンクが作成され、
関連付けれたモデルで指定されている全ての検証にパスすると保存されます。
@assembly = @part.assemblies.create({assembly_name: "Transmission housing"})
4.4.1.17 collection.create!(attributes = {})
collection.create
と同じことを行いますが、もし不正なレコードであれば、
ActiveRecord::RecordInvalid
が発生します。
4.4.2 has_and_belongs_to_manyのオプション
Railsが、ほとんどの状況に適した気の利いたデフォルト設定を使用してくれる一方、
has_and_belongs_to_many
関連付け参照の振る舞いを、カスタマイズしたいという事があるかもしれません。
そのようなカスタマイズは、関連付けを作成する際に、オプションを渡すことで簡単に行うことが可能です。
例えば、この関連付けは、そのような2つのオプションを使用します。
class Parts < ActiveRecord::Base
has_and_belongs_to_many :assemblies, autosave: true,
readonly: true
end
has_and_belongs_to_many
関連付けは、下記のオプションをサポートします。
:association_foreign_key
:autosave
:class_name
:foreign_key
:join_table
:validate
:readonly
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
オプションを使用して、モデル名を抵抗することが可能です。
例えば、パーツ(part)が多くの組み立て部品(assemblies)を持つが、
assembliesが含まれるモデルの実際の名前がGadget(ガジェット)である場合、
次のように設定することが出来ます。
class Parts < ActiveRecord::Base
has_and_belongs_to_many :assemblies, class_name: "Gadget"
end
4.4.2.4 :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.5 :join_table
もし結合テーブルの辞書順に基づいたデフォルトの名前が、指定したい名前と異なる場合、
:join_table
オプションを使用して、デフォルトの名前を上書きすることが出来ます。
4.4.2.6 :validate
:validate
オプションをfalse
に設定すると、
このオブジェクトを保存する際に関連付けオブジェクトの検証が行われなくなります。
デフォルトではこの値はtrue
で、関連付けられたオブジェクトは、このオブジェクトが保存される際に検証が行われます。
4.4.3 has_and_belongs_to_manyのスコープ
has_and_belongs_to_many
を使用する際に、クエリーをカスタマイズしたいというケースがあるかもしれません。
そのようなカスタマイズは、スコープのブロックを通して行うことが可能です。
例えば、
class Parts < ActiveRecord::Base
has_and_belongs_to_many :assemblies, -> { where active: true }
end
スコープブロック内では、標準のクエリーメソッドを幾つか使用することが可能です。 該当するものを下記に示します。
where
extending
group
includes
limit
offset
order
readonly
select
uniq
4.4.3.1 where
where
メソッドは、関連付くオブジェクトが満たすべき条件を指定することを可能にしてくれます。
class Parts < ActiveRecord::Base
has_and_belongs_to_many :assemblies,
-> { where "factory = 'Seattle'" }
end
またハッシュを通じて、条件を設定する事も可能です。
class Parts < ActiveRecord::Base
has_and_belongs_to_many :assemblies,
-> { where factory: 'Seattle' }
end
ハッシュ形式のwhere
を使用する場合、この関連付けを通したレコードの作成は、
自動的にそのハッシュを使用したスコープの対象になります。
このケースでは、@parts.assemblies.create
または@parts.assemblies.build
を使用すると、
factory
カラムに"Seattle"の値を持つordersが作成されます。
4.4.3.2 extending
extending
メソッドは、関連プロキシを拡張する名付けモジュールを指定します。
関連付けの拡張の詳細については、後ほどこのガイド内で説明します。
4.4.3.3 group
group
メソッドは、結果的にSQLでGROUP BY
の対象となる、
属性名を指定するのに使用されます。
class Parts < ActiveRecord::Base
has_and_belongs_to_many :assemblies, -> { group "factory" }
end
4.4.3.4 includes
この関連付けが使用される際に、テーブルの繋がりが2階層目として要求されるeager-load(熱心な読み込み)されるべきテーブルを、
includes
メソッドを使用することで、その関連付けを指定する事が可能です。
4.4.3.5 limit
limit
メソッドは、関連付けを通して取得されるオブジェクトの総数を制限してくれます。
class Parts < ActiveRecord::Base
has_and_belongs_to_many :assemblies,
-> { order("created_at DESC").limit(50) }
end
4.4.3.6 offset
offset
メソッドは、関連付けを通じて取得するオブジェクトの開始(オフセット)位置の指定を可能にしてくれます。
例えば、offset(11)
を設定すると、最初の11レコードをスキップします。
4.4.3.7 order
order
メソッドは、関連付けられて取得するオブジェクトの順序を指定します。
(ORDER BY
句で使用されるSQL文)
class Parts < ActiveRecord::Base
has_and_belongs_to_many :assemblies,
-> { order "assembly_name ASC" }
end
4.4.3.8 readonly
readonly
メソッドを使用すると、
関連付けを通じて取得する際に、関連付けられたオブジェクトは読み込み専用になります。
4.4.3.9 select
select
メソッドは、関連付けられたオブジェクトを取得する際の、SQL文のSELECT
句の上書きを可能にしてくれます。
デフォルトでは、Railsは全てのカラムを取得します。
4.4.3.10 uniq
コレクションから重複を削除するには、uniq
を使用してください。
4.4.4 When are Objects Saved?
オブジェクトへhas_and_belongs_to_many
関連付けを割り当てる際に、
オブジェクトは自動的に保存されます(結合テーブルを更新することによって)。
もし複数のオブジェクトを1文の中で割り当てたのであれば、それらの全てが保存されます。
もし、それらの内のいずれかのオブジェクトが、検証エラーのため保存に失敗したのであれば、
その割り当て文はfalse
を返し、その割り当て処理自身がキャンセルされます。
もし親となるオブジェクト(has_and_belongs_to_many
宣言を宣言しているオブジェクト)が保存されない(new_record?
がtrue
を返すという事)と、
子オブジェクトは追加された際に保存されません。
全ての保存されていない関連付けの項目は、親が保存された際に自動的に保存されます。
オブジェクトの保存をせずに、has_and_belongs_to_many
関連付けにオブジェクトを割り当てたい場合は、
collection.build
メソッドを使用してください。
4.5 関連付けのコールバック
通常のコールバックは、Activeレコードオブジェクトのライフサイクル内にフックされ、
それらのオブジェクトが様々なタイミングで動作させることを可能にします。
例えば、: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 関連付けの拡張
関連付け代理オブジェクト内に、Railsが自動的に構築する機能において、制限を受けることはありません。 また、これらのオブジェクトを、匿名のモジュールを通して、新しいファインダー(finder)、クリエーター(creater)、その他のメソッドを追加することで、 拡張することも可能です。 例えば、
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
下記のproxy_association
アクセサーの3つの属性を使用することで、
拡張は関連付け代理オブジェクトの内部を参照することが出来ます。
-
proxy_association.owner
は、関連付けの一部であるオブジェクトを返します。 -
proxy_association.reflection
は、関連付けを説明するリフレクションオブジェクトを返します。 -
proxy_association.target
は、belongs_to
、has_one
のために関連付けられたオブジェクト、 またはhas_many
,has_and_belongs_to_many
のための関連付けられた複数のオブジェクトを返します。
© 2010 - 2017 STUDIO KINGDOM