Rails – 最佳实践:如何创build相关的has_one关系

你能告诉我最好的做法是什么创buildhas_one关系?

如果我有一个用户模型,它必须有一个configuration文件…

我怎么能做到这一点?

一个解决scheme是:

# user.rb class User << ActiveRecord::Base after_create :set_default_association def set_default_association self.create_profile end end 

但是,这似乎不是很干净…任何build议?

创buildhas_one关系的最佳做法是使用ActiveRecordcallbackbefore_create而不是after_create 。 或者使用更早的callback,并处理未通过自己的validation步骤的问题(如果有的话)。

因为:

  • 通过良好的编码,如果validation失败,您就有机会向用户显示子logging的validation
  • 它更干净,ActiveRecord明确支持 – AR保存父logging(创build)后,AR自动填充子logging中的外键。 AR然后将子logging保存为创build父logging的一部分。

怎么做:

 # 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 

你的解决scheme绝对是一个体面的方式来做到这一点(至less在你长大了),但你可以简化它:

 # user.rb class User < ActiveRecord::Base has_one :profile after_create :create_profile end 

如果这是现有大型数据库中的新关联,那么我将像这样pipe理转换:

 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 

以便现有的用户logging在获得configuration文件时获得configuration文件,并用它创build新configuration文件。 这也将默认的属性放在一个地方,并正确使用rails4中的accep_nested_attributes_for。

可能不是最干净的解决scheme,但是我们已经有了一个拥有50万条logging的数据库,其中一些已经创build了“Profile”模型,其中一些没有。 我们采用这种方法,保证Profile模型在任何时候都可以使用,而不需要通过并追溯生成所有Profile模型。

 alias_method :db_profile, :profile def profile self.profile = Profile.create(:user => self) if self.db_profile.nil? self.db_profile end 

这是我怎么做的。 不知道这是如何标准的,但它工作得很好,懒惰的,它不会产生额外的开销,除非有必要build立新的关联(我很乐意纠正):

 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,但是这似乎增加了一些开销,因为每次对象被初始化时都会运行,并且可能有时候你不关心访问关联。 检查它的存在似乎是一种浪费。

这是一个gem:

https://github.com/jqr/has_one_autocreate

现在看起来有点老了。 (不适用于rails3)