Rails 7: has_one :through関連付けでコンストラクタが使えるようになる(翻訳)
RailsのActive Recordモデルでbelongs_to
関連付けを宣言すると、宣言したクラスで自動的にbuild_association
メソッドとcreate_association
のメソッドが使えるようになります。
たとえば以下の宣言があるとします。
class Book < ApplicationRecord
belongs_to :author
end
このとき、Bookモデルの各インスタンスでbuild_author
メソッドとcreate_author
メソッドが使えるようになります。
今度は以下の3つのモデルで考えてみましょう。
# app/models/supplier.rb
class Supplier < ApplicationRecord
has_one :account
has_one :account_history, through: :account
end
# app/models/account.rb
class Account < ApplicationRecord
belongs_to :supplier
has_one :account_history
end
# app/models/account_history.rb
class AccountHistory < ApplicationRecord
belongs_to :account
end
サプライヤーのアカウントを作成する場合は、以下のようにSupplierオブジェクトでcreate_account
を呼べます。
Supplier.first.create_account
# Supplier Load (0.5ms) SELECT "suppliers".* FROM "suppliers" ORDER BY "suppliers"."id" ASC LIMIT $1 [["LIMIT", 1]]
# Account Load (0.5ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."supplier_id" = $1 LIMIT $2
[["supplier_id", "6700d0ba-b8ed-4ff5-bb69-c57e7ea7f285"], ["LIMIT", 1]]
has_one: :account
という直接の関連付けは期待どおりに動きますが、AccountHistoryを作成しようとすると以下のようにエラーになります。
Supplier.first.create_account_history
# Supplier Load (0.5ms) SELECT "suppliers".* FROM "suppliers" ORDER BY "suppliers"."id" ASC LIMIT $1 [["LIMIT", 1]]
NoMethodError: undefined method `create_account_history' for #<Supplier:0x00007f949d6016c8>
このように、has_one :through
リレーションシップを作成してもcreate_account_history
メソッドは使えるようになりません。
変更前
Ruby 7より前は、Supplier
モデルにcreate_account_history
メソッドを自分で追加することで上の問題を解決できます。
class Supplier < ApplicationRecord
has_one :account
has_one :account_history, through: :account
def create_account_history
account = self.account
account.create_account_history
end
end
これは期待どおり動作しますが、他のhas_one :through
関連付けでも同様のメソッドを自分で追加することになります。
変更後
Rails 7ではこの問題を解決するため、has_one :through
関連付けでbuild_<関連付け名>
メソッドとcreate_<関連付け名>
メソッドが使えるようになりました(#40007)。
以下のようにcreate_account_history
を書けば、Supplier
オブジェクトでAccountHistoryを作成できます。
Supplier.first.create_account_history
# AccountHistory Create (1.3ms) INSERT INTO "account_histories" ("account_id", "user_id") VALUES ($1, $2) RETURNING "id" [["account_id", "5d671a08-1513-442a-8cfa-ac00e128d423"], ["user_id", "24571aff-3456-442a-8cfa-aerte128d423"], ["organization_user_id""6700d0ba-b8ed-4ff5-bb69-c57e7ea7f285"], ["created_at", "2021-04-09 06:26:59.002535"], ["updated_at", "2021-04-09 06:26:59.002535"]]
build_account_history
も同様に利用でき、NoMethodError
例外が発生しなくなります。
概要
原著者の許諾を得て翻訳・公開いたします。