Techioz Blog

関連モデルの作成中に不必要なロードを回避するにはどうすればよいですか?

概要

私の Rails アプリには、これに似たモデルがいくつかあります。

class Pen < ActiveRecord::Base
  has_one :ink
  after_create :add_ink

  def add_ink
    create_ink(color: "blue")
  end
end

class Ink < ActiveRecord::Base
  belongs_to :pen
end

コントローラーでは次のようなことを行います

pen = Pen.new(..)
pen.save

概念的には、ペンには常にインクがあり、ペンの作成時にインクも作成されます。 (これを達成するための after_create フックよりも優れた方法にもオープンです)。

問題は、関連付けには何もないことが保証されているにもかかわらず、関連付けに対して常に SELECT を発行していることです。

[DEBUG]   TRANSACTION (0.2ms)  BEGIN
[DEBUG]   Pen Create (1.5ms)  INSERT INTO pens ("owner_id") VALUES ('00f74077-c079-4482-aa03-15b23951a7bd') RETURNING "id"
[DEBUG]   Ink Load (0.4ms)  SELECT "inks".* FROM "inks" WHERE "inks"."pen_id" = '93a45bae-2cf3-48c1-ac00-6b3059dca5ae' LIMIT 1
[DEBUG]   Ink Create (0.3ms)  INSERT INTO "inks" ("pen_id", "color") VALUES ('93a45bae-2cf3-48c1-ac00-6b3059dca5ae', 'blue') RETURNING "pen_id"
[DEBUG]   TRANSACTION (0.2ms)  COMMIT

具体的には、このようなことは起こらないはずです

[DEBUG]   Ink Load (0.4ms)  SELECT "inks".* FROM "inks" WHERE "inks"."pen_id" = '93a45bae-2cf3-48c1-ac00-6b3059dca5ae' LIMIT 1

build_association の後に save、create_association を使用したり、Association.create で association= を使用したりしてみましたが、いずれもある時点で不要な Load が発生します。

解決策

表示されている現象は、after_create で Ink が作成されたときに Pen がすでに永続化されていることが原因で発生します。

ドキュメントから

したがって、あなたが見ているのは、Rails が関連するレコードがすでに存在するかどうかを判断し、それに応じて更新できるようにしようとしているということです。

希望する結果に応じて、 Ink.create(pen_id: self.id, color: ‘blue’); を試すことができます。ただし、これを before_create アクションに移動するだけで、おそらくより良い実装になると思います。

class Pen < ActiveRecord::Base
  has_one :ink
  before_create :add_ink

  def add_ink
    build_ink(color: "blue")
  end
end

ドキュメントによると

したがって、これは before_create であるため、ペンは新しいレコードであるため、データベースにクエリせずにインクも保存する必要があります。