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)
此代码未经测试,但它应该是您所需要的.
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回调中.
使用 时: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_很有趣。我一定会用它!但是,也请考虑这个更简单的解决方案。
一个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)