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