18

ユーザーが一連の設定を行えるアプリケーションがあります。どちらも次のように ActiveRecord モデルとして保存されます。

class User < AR::Base
   has_one :preference_set
end

class PreferenceSet < AR::Base
   belongs_to :user
end

ユーザーの設定にアクセスできるようになりました。

@u = User.first
@u.preference_set => #<PreferenceSet...>
@u.preference_set.play_sounds => true

しかし、 @u.preference_set が nil を返し、play_soundsonを呼び出すため、設定セットがまだ作成されていない場合、これは失敗しますnil

アーカイブしたいのは、 User.preference_set が常に PreferenceSet インスタンスを返すことです。私はそれを次のように定義しようとしました:

class User < ..
   has_one :preference_set

   def preference_set
     preference_set || build_preference_set
   end
end

'Stack level too deep'再帰的に自分自身を呼び出しているため、これは を引き起こしています。

私の質問はこれです:

対応するpreference_set-recordを@user.preference_set返すか、存在しない場合は新しいレコードを作成するにはどうすればよいですか?

アソシエーションの名前を変更するだけでよいことはわかっていますが (例: preference_set_real)、この方法で再帰呼び出しを回避できますが、アプリをシンプルにするために、この名前をそのまま使用したいと思います。

ありがとう!

4

2 に答える 2

57

エレガントでシンプルなフォームがあります:

class User < ApplicationRecord
  has_one :preference_set
  
  def preference_set
    super || build_preference_set
  end
end

ActiveRecordは、関連付けメソッドの動作をオーバーライドするために、意図的にそのような使用を可能にします。super

于 2013-07-24T05:38:45.010 に答える
30

これを行う最善の方法は、プライマリ レコードを作成するときに関連するレコードを作成することです。

class User < ActiveRecord::Base
   has_one       :preference_set, :autosave => true
   before_create :build_preference_set
end

それはそれを設定するので、Userが作成されるたびに、 も作成されPreferenceSetます。関連付けられたレコードを引数で初期化する必要がある場合は、別のメソッドbefore_createを呼び出しbuild_preference_set(:my_options => "here")て、 を呼び出してから を返しますtrue

次に、 を持たないものを反復処理し、 をPreferenceSet呼び出して作成することで、既存のすべてのレコードを正規化でき#create_preference_setます。

絶対に必要な場合にのみ作成したい場合PreferenceSetは、次のようにすることができます。

class User < ActiveRecord::Base
   has_one :preference_set

   def preference_set_with_initialize
     preference_set_without_initialize || build_preference_set
   end

   alias_method_chain :preference_set, :initialize
end
于 2010-09-28T13:01:16.127 に答える