Rails:忽略传递给create()的不存在的属性
我有以下Rails模型:
class CreateFoo < ActiveRecord::Migration def self.up create_table :foo do |t| t.string :a t.string :b t.string :c t.timestamps end end def self.down drop_table :foo end end
如果我尝试使用其他不存在的属性创建新记录,则会产生错误:
Foo.create(a: 'some', b: 'string', c: 'foo', d: 'bar') ActiveRecord::UnknownAttributeError: unknown attribute: d
有没有办法让create()忽略模型中不存在的属性? 或者,在创建新记录之前删除不存在的属性的最佳方法是什么?
非常感谢
试着想出一种可能更有效的方法,但现在:
hash = { :a => 'some', :b => 'string', :c => 'foo', :d => 'bar' } @something = Something.new @something.attributes = hash.reject{|k,v| !@something.attributes.keys.member?(k.to_s) } @something.save
我经常使用(简化):
params.select!{|x| Model.attribute_names.index(x)} Model.update_attributes(params)
我刚刚将这个确切的问题升级到Rails 3.2,当我设置:
config.active_record.mass_assignment_sanitizer = :strict
它引起了我的一些创造! 调用失败,因为先前被忽略的字段现在导致批量分配错误。 我通过伪造模型中的字段来解决这个问题,如下所示:
attr_accessor :field_to_exclude attr_accessible :field_to_exclude
Re:有没有办法让create()忽略模型中不存在的属性? – 不,这是设计的。
您可以创建将由create使用的attr_setter –
attr_setter :a # will silently absorb additional parameter 'a' from the form.
Re:或者,在创建新记录之前删除不存在的属性的最佳方法是什么?
您可以明确删除它们:
params[:Foo].delete(:a) # delete the extra param :a
但最好的办法是不要把它们放在首位。 修改您的表单以省略它们。
添加:
鉴于更新的信息(传入数据),我想我会创建一个新的哈希:
incoming_data_array.each{|rec| Foo.create {:a => rec['a'], :b => rec['b'], :c => rec['c']} # create new # rec from specific # fields }
添加更多
# Another way: keepers = ['a', 'b', 'c'] # fields used by the Foo class. incoming_data_array.each{|rec| Foo.create rec.delete_if{|key, value| !keepers.include?(key)} # create new rec } # from kept # fields
我想出了一个看起来像这样的解决方案,你可能会发现它很有帮助:
def self.create_from_hash(hash) hash.select! {|k, v| self.column_names.include? k } self.create(hash) end
这对我来说是一个理想的解决方案,因为在我看来, hash
来自一个理想的数据源,它反映了我的模式(除了有其他字段)。
我认为在Foo的模型类中使用attr_accessible方法可以达到你想要的效果,例如:
class Foo < ActiveRecord::Base attr_accessible :a, :b, :c ... end
这将允许仅设置/更新使用attr_accessible列出的那些属性。
我找到了一个运行良好的解决方案,并且最终是上述选项的组合。 它允许传递(并忽略)无效参数,而有效参数则正确映射到对象。
def self.initialize(params={}) User.new(params.reject { |k| !User.attribute_method?(k) }) end
现在,而不是调用User.new()
,调用User.new()
User.initialize()
。 这将相当优雅地“过滤”正确的参数。
您可以使用Hash#slice
和column_names
方法也作为类方法存在。
hash = {a: 'some', b: 'string', c: 'foo', d: 'bar'} Foo.create(hash.slice(*Foo.column_names.map(&:to_sym)))