Rails:忽略传递给create()的不存在的属性

时间:2010-11-08 21:23:35

标签: ruby-on-rails ruby-on-rails-3

我有以下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()忽略模型中不存在的属性?或者,在创建新记录之前删除不存在的属性的最佳方法是什么?

非常感谢

8 个答案:

答案 0 :(得分:36)

尝试考虑一种可能更有效的方法,但目前:

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

答案 1 :(得分:10)

我经常使用(简化):

params.select!{|x| Model.attribute_names.index(x)}
Model.update_attributes(params)

答案 2 :(得分:7)

当我设置时,我刚刚将此问题升级到Rails 3.2:

config.active_record.mass_assignment_sanitizer = :strict

它引起了我的一些创造!调用失败,因为先前被忽略的字段现在导致批量分配错误。我通过伪造模型中的字段来解决这个问题:

attr_accessor   :field_to_exclude
attr_accessible :field_to_exclude

答案 3 :(得分:6)

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

答案 4 :(得分:3)

我提出了一个看起来像这样的解决方案,你可能会发现它很有用:

def self.create_from_hash(hash)
  hash.select! {|k, v| self.column_names.include? k }
  self.create(hash)
end

这对我来说是一个理想的解决方案,因为在我的情况下hash来自一个理想的数据源,它反映了我的模式(除了有其他字段)。

答案 5 :(得分:1)

我认为在Foo的模型类中使用 attr_accessible 方法可以达到你想要的效果,例如:

class Foo < ActiveRecord::Base

  attr_accessible :a, :b, :c

  ...
end

这样只允许设置/更新 attr_accessible 列出的属性。

答案 6 :(得分:0)

我找到了一个运行良好的解决方案,并且最终是上述选项的组合。它允许传递(并忽略)无效参数,而有效参数则正确映射到对象。

def self.initialize(params={})
  User.new(params.reject { |k| !User.attribute_method?(k) })
end

现在,不要致电User.new(),而是致电User.initialize()。这将相当优雅地“过滤”正确的参数。

答案 7 :(得分:0)

您可以使用Hash#slicecolumn_names方法作为类方法。

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