Rails - 最佳实践:如何创建依赖的has_one关系

Bvu*_*Ic7 74 ruby activerecord ruby-on-rails associations ruby-on-rails-3

你能告诉我创建has_one关系的最佳做法吗?

如果我有一个用户模型,它必须有一个配置文件...

我怎么能做到这一点?

一个解决方案是:

# user.rb
class User << ActiveRecord::Base
  after_create :set_default_association

  def set_default_association
    self.create_profile
  end
end
Run Code Online (Sandbox Code Playgroud)

但这似乎不是很干净......有什么建议吗?

Lar*_*y K 123

创建has_one关系的最佳做法是使用ActiveRecord回调before_create而不是after_create.或者使用更早的回调并处理未通过自己的验证步骤的孩子的问题(如果有的话).

因为:

  • 如果验证失败,您可以通过良好的编码,向用户显示子记录的验证
  • 它更干净,并且由ActiveRecord明确支持 - AR在保存父记录(创建时)后自动填充子记录中的外键.然后,AR将子记录保存为创建父记录的一部分.

怎么做:

# 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
Run Code Online (Sandbox Code Playgroud)

  • 只要注意Rails 5使用before_create创建依赖记录是不可能的,而不会覆盖belongs_to记录的默认值.默认情况下,现在期望belongs_to记录存在,否则将引发错误. (9认同)
  • @Lichtamberg:是的,但我要添加评论:"构建默认配置文件.必须始终验证." 注意:它是"before_create:build_profile"而不是'before_filter'.如果它没有验证,那么你会收到一个非常令人困惑的错误消息给用户.或者它实际上不会被创建,这意味着你最终会得到一个没有个人资料的用户.您还应该测试测试中的极端情况. (2认同)

Bo *_*nes 28

你的解决方案绝对是一个不错的方法(至少在你长大之前),但你可以简化它:

# user.rb
class User < ActiveRecord::Base
  has_one      :profile
  after_create :create_profile
end
Run Code Online (Sandbox Code Playgroud)


ino*_*tus 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
Run Code Online (Sandbox Code Playgroud)

以便现有用户记录在被要求时获得一个配置文件,并使用它创建新的用户记录.这也将默认属性放在一个位置,并在Rails 4之后使用accepts_nested_attributes_for正常工作.


And*_*sak 8

可能不是最干净的解决方案,但我们已经拥有一个拥有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
Run Code Online (Sandbox Code Playgroud)


Bre*_*uir 5

这是我的方法。不确定这是什么标准,但是它工作得很好,并且很懒惰,除非有必要建立新的关联,否则它不会产生额外的开销(我很乐意对此进行纠正):

def profile_with_auto_build
  build_profile unless profile_without_auto_build
  profile_without_auto_build
end

alias_method_chain :profile, :auto_build
Run Code Online (Sandbox Code Playgroud)

这也意味着该关联会在您需要时立即存在。我猜是替代方法是挂在after_initialize上,但这似乎增加了很多开销,因为每次初始化对象时都会运行它,并且有时您不关心访问该关联。检查它的存在似乎是浪费。