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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3808782/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 23:16:16  来源:igfitidea点击:

Rails - Best-Practice: How to create dependent has_one relations

ruby-on-railsrubyactiverecordruby-on-rails-3associations

提问by BvuRVKyUVlViVIc7

Could you tell me whats the best practice to create has_one relations?

你能告诉我创建 has_one 关系的最佳实践是什么吗?

f.e. if i have a user model, and it must have a profile...

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

How could i accomplish that?

我怎么能做到这一点?

One solution would be:

一种解决方案是:

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

  def set_default_association
    self.create_profile
  end
end

But that doesnt seem very clean... Any suggests?

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

回答by Larry K

Best practice to create has_one relation is to use the ActiveRecord callback before_createrather than after_create. Or use an even earlier callback and deal with the issues (if any) of the child not passing its own validation step.

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

Because:

因为:

  • with good coding, you have the opportunity for the child record's validations to be shown to the user if the validations fail
  • it's cleaner and explicitly supported by ActiveRecord -- AR automagically fills in the foreign key in the child record after it saves the parent record (on create). AR then saves the child record as part of creating the parent record.
  • 通过良好的编码,如果验证失败,您就有机会向用户显示子记录的验证
  • 它更清晰,并由 ActiveRecord 明确支持——AR 在保存父记录(创建时)后自动填充子记录中的外键。然后 AR 将子记录保存为创建父记录的一部分。

How to do it:

怎么做:

# 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

回答by Bo Jeanes

Your solution is definitely a decent way to do it (at least until you outgrow it), but you can simplify it:

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

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

回答by inopinatus

If this is a new association in an existing large database, I'll manage the transition like this:

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

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

so that existing user records gain a profile when asked for it and new ones are created with it. This also places the default attributes in one place and works correctly with accepts_nested_attributes_for in Rails 4 onwards.

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

回答by Andrew Vilcsak

Probably not the cleanest solution, but we already had a database with half a million records, some of which already had the 'Profile' model created, and some of which didn't. We went with this approach, which guarantees a Profile model is present at any point, without needing to go through and retroactively generate all the Profile models.

可能不是最干净的解决方案,但我们已经有一个包含 50 万条记录的数据库,其中一些已经创建了“配置文件”模型,而另一些没有。我们采用了这种方法,它保证在任何时候都存在 Profile 模型,而无需遍历并追溯生成所有 Profile 模型。

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

回答by Brendon Muir

Here's how I do it. Not sure how standard this is, but it works very well and its lazy in that it doesn't create extra overhead unless it's necessary to build the new association (I'm happy to be corrected on this):

这是我如何做到的。不确定这是多么标准,但它工作得很好并且它很懒惰,因为它不会产生额外的开销,除非有必要建立新的关联(我很高兴得到纠正):

def profile_with_auto_build
  build_profile unless profile_without_auto_build
  profile_without_auto_build
end

alias_method_chain :profile, :auto_build

This also means that the association is there as soon as you need it. I guess the alternative is to hook into after_initialize but this seems to add quite a bit of overhead as it's run every time an object is initialized and there may be times where you don't care to access the association. It seems like a waste to check for its existence.

这也意味着只要您需要,该协会就会在那里。我想另一种方法是挂钩 after_initialize 但这似乎增加了相当多的开销,因为每次初始化对象时它都会运行,并且有时您可能不关心访问关联。检查它的存在似乎是一种浪费。

回答by linjunhalida

There is a gem for this:

有一个宝石:

https://github.com/jqr/has_one_autocreate

https://github.com/jqr/has_one_autocreate

Looks like it is a bit old now. (not work with rails3)

现在好像有点旧了。(不适用于 rails3)

回答by kkelleey

I had an issue with this and accepts_nested_attributes_for because if nested attributes were passed in, the associated model was created there. I ended up doing

我遇到了这个问题并接受了_nested_attributes_for,因为如果传入了嵌套属性,则会在那里创建关联的模型。我最终做了

after_create :ensure_profile_exists
has_one :profile
accepts_nested_attributes_for :profile


def ensure_profile_exists
  profile || create_profile
end