Has_one関係を作成するためのベストプラクティスを教えてください。
f.e.ユーザーモデルがあり、プロファイルが必要な場合...
どうすればそれを達成できますか?
1つの解決策は次のとおりです。
# user.rb
class User << ActiveRecord::Base
after_create :set_default_association
def set_default_association
self.create_profile
end
end
しかし、それはあまりきれいではないようです...何か提案はありますか?
Has_one関係を作成するためのベストプラクティスは、ActiveRecordコールバックbefore_create
のではなく after_create
。または、さらに早いコールバックを使用して、子が自身の検証ステップを渡さないという問題(ある場合)に対処します。
なぜなら:
方法:
# in your User model...
has_one :profile
before_create :build_default_profile
private
def build_default_profile
# build default profile instance. Will use default params.
# The foreign key to the owning User model is set automatically
build_profile
true # Always return true in callbacks as the normal 'continue' state
# Assumes that the default_profile can **always** be created.
# or
# Check the validation of the profile. If it is not valid, then
# return false from the callback. Best to use a before_validation
# if doing this. View code should check the errors of the child.
# Or add the child's errors to the User model's error array of the :base
# error item
end
あなたのソリューションは間違いなくそれを行うにはまともな方法ですが(少なくともあなたがそれを上回るまで)、それを単純化することができます:
# user.rb
class User < ActiveRecord::Base
has_one :profile
after_create :create_profile
end
これが既存の大規模データベースの新しい関連付けである場合、次のように移行を管理します。
class User < ActiveRecord::Base
has_one :profile
before_create :build_associations
def profile
super || build_profile(avatar: "anon.jpg")
end
private
def build_associations
profile || true
end
end
既存のユーザーレコードは、要求されたときにプロファイルを取得し、新しいプロファイルが作成されます。これにより、デフォルトの属性も1か所に配置され、accepts_nested_attributes_for in Rails 4以降で正しく動作します。
おそらく最もクリーンなソリューションではありませんが、50万件のレコードを持つデータベースが既にあり、その一部は既に「プロファイル」モデルを作成しており、一部は作成していません。このアプローチでは、すべてのプロファイルモデルを遡って生成する必要なく、プロファイルモデルが任意の時点で存在することを保証します。
alias_method :db_profile, :profile
def profile
self.profile = Profile.create(:user => self) if self.db_profile.nil?
self.db_profile
end
ここに私がそれをする方法があります。これがどれほど標準かはわかりませんが、非常にうまく機能し、新しい関連付けを作成する必要がない限り、余分なオーバーヘッドを作成しないという点で怠zyです(これについて修正できてうれしいです):
def profile_with_auto_build
build_profile unless profile_without_auto_build
profile_without_auto_build
end
alias_method_chain :profile, :auto_build
これは、必要に応じてすぐに関連付けが存在することも意味します。代替手段はafter_initializeにフックすることですが、オブジェクトが初期化されるたびに実行されるため、かなりのオーバーヘッドが追加されるようです。関連付けにアクセスしたくない場合もあるでしょう。その存在を確認するのはもったいないようです。