你能告诉我创建has_one关系的最佳做法吗?
f.e。如果我有一个用户模型,它必须有个人资料......
我怎么能做到这一点?
一种解决方案是:
# user.rb
class User << ActiveRecord::Base
after_create :set_default_association
def set_default_association
self.create_profile
end
end
但这看起来不是很干净......有什么建议吗?
答案 0 :(得分:118)
创建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
答案 1 :(得分:27)
你的解决方案绝对是一种不错的方式(至少在你不能实现之前),但你可以简化它:
# user.rb
class User < ActiveRecord::Base
has_one :profile
after_create :create_profile
end
答案 2 :(得分:22)
如果这是现有大型数据库中的新关联,我将按照以下方式管理转换:
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
以便现有用户记录在被要求时获得一个配置文件,并使用它创建新的用户记录。这也将默认属性放在一个位置,并在Rails 4之后与accepts_nested_attributes_for一起正常工作。
答案 3 :(得分:7)
可能不是最干净的解决方案,但我们已经拥有一个拥有50万条记录的数据库,其中一些已经创建了“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
答案 4 :(得分:4)
我是这样做的。不确定这是多么标准,但它工作得很好而且它很懒,因为它不会产生额外的开销,除非有必要建立新的关联(我很乐意对此进行纠正):
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,但这似乎增加了相当多的开销,因为每次初始化对象时它都会运行,并且有时你可能不关心访问关联。检查它的存在似乎是一种浪费。
答案 5 :(得分:1)
答案 6 :(得分:0)
我对此有一个问题,并接受accepts_nested_attributes_for,因为如果传入嵌套属性,则会在此处创建关联的模型。我结束了
after_create :ensure_profile_exists
has_one :profile
accepts_nested_attributes_for :profile
def ensure_profile_exists
profile || create_profile
end