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#slicecolumn_names方法也作为类方法存在。

 hash = {a: 'some', b: 'string', c: 'foo', d: 'bar'} Foo.create(hash.slice(*Foo.column_names.map(&:to_sym)))