web-dev-qa-db-ja.com

Rails after "initialize only on" new "

私は次の2つのモデルを持っています

class Sport < ActiveRecord::Base
  has_many :charts, order: "sortWeight ASC"
  has_one :product, :as => :productable
  accepts_nested_attributes_for :product, :allow_destroy => true
end

class Product < ActiveRecord::Base
  belongs_to :category
  belongs_to :productable, :polymorphic => true
end

製品なしではスポーツは存在できないので、私のsports_controller.rbには以下がありました。

def new
  @sport = Sport.new
  @sport.product = Product.new
...
end

after_initializeを使用して、製品の作成をスポーツモデルに移行しようとしました。

after_initialize :create_product

def create_product
 self.product = Product.new
end

モデルがインスタンス化されるたびに(つまり、find呼び出しから)after_initializeが呼び出されることをすぐに知りました。ですから、それは私が探していた行動ではありませんでした。

すべてのsportproductがあるという要件をモデル化する方法は何ですか?

ありがとう

49
Tyler DeWitt

あなたが述べたように、コントローラにロジックを入れることが最良の答えかもしれませんが、after_initialize以下を実行して機能します。

after_initialize :add_product

def add_product
  self.product ||= Product.new
end

そのようにして、製品が存在しない場合にのみ製品を設定します。オーバーヘッドの価値がないか、コントローラにロジックがあるよりも明確でない場合があります。

編集:ライアンの答えによると、パフォーマンスの面では次の方が良いでしょう。

after_initialize :add_product

def add_product
  self.product ||= Product.new if self.new_record?
end
63
bostonou

確かにafter_initialize :add_product, if: :new_record?は、ここで最もクリーンな方法です。

条件をadd_product関数から除外する

40
Paul Odeon

もしあなたがそうするなら self.product ||= Product.newは、findを実行するたびに製品を検索します。nilかどうかを確認する必要があるためです。その結果、積極的な読み込みは行われません。これを行うには、新しいレコードが作成されたときにのみ、製品を設定する前にそれが新しいレコードであるかどうかを確認するだけで済みます。

after_initialize :add_product

def add_product
  self.product ||= Product.new if self.new_record?
end

基本的なベンチマークとチェックを行ったif self.new_record?は、目立った形でパフォーマンスに影響を与えないようです。

27
Ryan

after_initializeを使用する代わりに、after_createはどうですか?

after_create :create_product

def create_product
  self.product = Product.new
  save
end

それはあなたの問題を解決するように見えますか?

2
James Brooks

とても近いようです。 after_initializeの呼び出しを完全に廃止できるはずですが、最初に、スポーツモデルが指定した:productと「has_one」関係にある場合、製品モデルも「belong_to」スポーツでなければなりません。これを製品モデルに追加します

belongs_to: :sport

次のステップでは、スポーツモデルを次のようにインスタンス化できるはずです。

@sport = @product.sport.create( ... )

これは、 Association Basics from Ruby on Railsガイド。正確ではない

1
coderates

あなただけのような初期化メソッドをオーバーライドする必要があります

class Sport < ActiveRecord::Base

  # ...

  def initialize(attributes = {})
    super
    self.build_product
    self.attributes = attributes
  end

  # ...

end

データベースからレコードがロードされるときに、Initializeメソッドが呼び出されることはありません。上記のコードでは、製品のビルド後に属性が割り当てられていることに注意してください。このような設定では、属性の割り当ては作成された製品インスタンスに影響を与える可能性があります。

0
Victor Nazarov