使用find_or_create接受_ented_attributes_for?

tri*_*nia 54 forms validation activerecord ruby-on-rails

我正在使用Rails的accepts_nested_attributes_for方法取得了巨大的成功,但是如果记录已经存在,我怎么能创建新的记录呢?

举例来说:

假设我有三个模型,团队,会员和玩家,每个团队都有很多玩家通过会员资格,玩家可以属于很多团队.然后,团队模型可以接受玩家的嵌套属性,但这意味着通过组合团队+玩家形式提交的每个玩家将被创建为新的玩家记录.

如果我只想以这种方式创建一个新的播放器记录,如果还没有同名的播放器,我应该怎么做呢?如果具有相同名称的玩家,没有新玩家记录应当建立,而是正确的玩家应该发现并用新的球队纪录有关.

Fra*_*eil 55

为自动保存关联定义挂钩时,将跳过正常的代码路径,而是调用您的方法.因此,你可以这样做:

class Post < ActiveRecord::Base
  belongs_to :author, :autosave => true
  accepts_nested_attributes_for :author

  # If you need to validate the associated record, you can add a method like this:
  #     validate_associated_record_for_author
  def autosave_associated_records_for_author
    # Find or create the author by name
    if new_author = Author.find_by_name(author.name)
      self.author = new_author
    else
      self.author.save!
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

此代码未经测试,但它应该是您所需要的.

  • 这个解决方案对我来说并不适用(Rails 3.2).我和@Ashitaka有类似的问题,没有为子对象设置ID.在上面的例子中,你需要在`self.author.save之后添加一行!'说:`self.author_id = self.author.id`.@maletor你可以赞成这一点,以便其他人可以看到它 - 它正在"显示更多" - ty消失. (17认同)
  • 另外我认为正确的是def autosave_associated_records_for_author. (5认同)
  • 我无法在文档中的任何地方找到它,但是从代码中可以清楚地看到它应该被覆盖:http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html和http://github.com/导轨/导轨/ BLOB/2-3稳定/ ActiveRecord的/ lib目录/ active_record/autosave_association.rb#L168 (3认同)

rya*_*anb 30

不要将其视为向团队添加玩家,将其视为向团队添加成员资格.该表单不能直接与玩家一起使用.Membership模型可以具有player_name虚拟属性.在幕后,这可以查找玩家或创建一个.

class Membership < ActiveRecord::Base
  def player_name
    player && player.name
  end

  def player_name=(name)
    self.player = Player.find_or_create_by_name(name) unless name.blank?
  end
end
Run Code Online (Sandbox Code Playgroud)

然后只需将player_name文本字段添加到任何"成员身份"表单构建器.

<%= f.text_field :player_name %>
Run Code Online (Sandbox Code Playgroud)

这种方式并不特定于accepts_nested_attributes_for,可以用于任何成员资格形式.

注意:使用此技术,在验证发生之前创建Player模型.如果您不想要此效果,则将播放器存储在实例变量中,然后将其保存在before_save回调中.

  • 关于该注释:如果您不想在验证之前创建Player,请使用`find_or_initialize_by_name(name)`而不是`find_or_create_by_name(name)` (3认同)

Ans*_*son 5

使用 时:accepts_nested_attributes_for,提交id现有记录将导致 ActiveRecord更新现有记录而不是创建新记录。我不确定你的标记是什么样的,但尝试大致如下:

<%= text_field_tag "team[player][name]", current_player.name %>
<%= hidden_field_tag "team[player][id]", current_player.id if current_player %>
Run Code Online (Sandbox Code Playgroud)

如果提供了玩家名称,则将更新玩家名称id,否则将创建玩家名称。

定义方法的方式autosave_associated_record_for_很有趣。我一定会用它!但是,也请考虑这个更简单的解决方案。


vem*_*emv 5

一个before_validation挂钩是一个不错的选择:这是导致比覆盖更隐晦更简单的代码的标准机制autosave_associated_records_for_*

class Quux < ActiveRecord::Base

  has_and_belongs_to_many :foos
  accepts_nested_attributes_for :foos, reject_if: ->(object){ object[:value].blank? }
  before_validation :find_foos

  def find_foos
    self.foos = self.foos.map do |object|
      Foo.where(value: object.value).first_or_initialize
    end
  end

end
Run Code Online (Sandbox Code Playgroud)