assign_attributes 不覆盖 not nil 数据
assign_attributes without overwriting not nil data
有没有属性覆盖的 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)
有什么想法吗?
这对你有用吗?
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))
如评论中所述,我认为 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
方法接受两个参数(与我们的新参数相同)。这意味着您还需要将选项传递给对 super.
的每次调用
有没有属性覆盖的 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)
有什么想法吗?
这对你有用吗?
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))
如评论中所述,我认为 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
方法接受两个参数(与我们的新参数相同)。这意味着您还需要将选项传递给对 super.