assign_attributes而不覆盖非零数据

时间:2015-01-07 10:43:43

标签: ruby-on-rails

是否有相当于assign_attributes没有属性覆盖的内容?

让我用一个简单的例子解释一下:

my_model = MyModel.new(first_name: 'Romain', age: nil)

new_attributes = {first_name: 'Pierre', last_name: 'Roger', age: 27}
my_model.assign_attributes(new_attributes)
# What I get : <first_name='Pierre', last_name='Roger', age=27>
#  * Romain is overwritten by Pierre
#  * nil is overwritten by 27
# What I would like : <first_name='Romain', last_name='Roger', age=27>
#  * Romain isn't overwritten by Pierre
#  * nil is overwritten by 27

我知道我可以做这样的事情,但这似乎不对:

new_attributes.merge(
    my_model.slice(:first_name, :last_name, :age)
            .select { |_, val| !val.nil? }
)
my_model.assign_attributes(new_attributes)

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

正如评论中所提到的,我不认为ActiveRecord中有一个可以使用的方法来执行此操作。但是,您可以编写自己的,甚至更好的 - 修改现有的assign_attributes方法。当然,我们需要格外小心,不要破坏它的基本功能 - 我们只会稍微扩展它。

assign_attributes方法在ActiveRecord::AttributeAssignment模块中定义,然后包含在ActiveRecord::Base中。我们可以直接在其模块中覆盖它,但是对于新模块来说它似乎更清晰,而是将其包含在ActiveRecord::Base中(因为我们可以调用super,而不是别名链接)。将以下代码复制到config/initializers中的新文件(任何名称都可以):

module ActiveRecord::AttributeAssignmentOverride
  def assign_attributes(new_attributes, options={})
    return super(new_attributes) if options.fetch(:override, true)
    super(new_attributes.select {|k,_| self[k].nil? })
  end
end

class ActiveRecord::Base
  include ActiveRecord::AttributeAssignmentOverride
end

然后您可以按常规方式使用assign_attributes(自然重启后):

model.assign_attributes(new_params)

将照常工作,覆盖所有非零值。但是,您现在可以添加额外选项:

 model.assign_attributes(new_params, override: false)

不会触及已经指定的值。

疑难杂症

上面的代码只能在rails 4中使用.Rails 3使用attr_accessible代替强参数来进行质量分配安全性。这些可能取决于许多条件,因此在rails 3中,默认assign_attributes方法接受两个参数(与我们的新参数相同)。这意味着您还需要将选项传递给每次超级调用。

答案 1 :(得分:0)

这对你有用吗?

my_model = MyModel.new(first_name: 'Romain', age: nil)
new_attributes = {first_name: 'Pierre', last_name: 'Roger', age: 27}

my_model.assign_attributes(my_model.attributes.reject{|k,v|v.nil?}.reverse_merge(new_attributes))