代码之家  ›  专栏  ›  技术社区  ›  gjb

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

  •  26
  • gjb  · 技术社区  · 14 年前

    我有以下轨道型号:

    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 回复  |  直到 8 年前
        1
  •  36
  •   jenjenut233    14 年前

    试图想出一个更有效的方法,但现在:

    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
    
        2
  •  10
  •   user1919149    12 年前

    我经常使用这个(简化的):

    params.select!{|x| Model.attribute_names.index(x)}
    Model.update_attributes(params)
    
        3
  •  7
  •   Amir Rubin    12 年前

    我刚在升级到Rails 3.2时遇到了这个问题,当时我设置了:

    config.active_record.mass_assignment_sanitizer = :strict
    

    它引起了我的一些创作!调用失败,因为以前被忽略的字段现在会导致大量分配错误。我通过模拟模型中的字段来解决这个问题,如下所示:

    attr_accessor   :field_to_exclude
    attr_accessible :field_to_exclude
    
        4
  •  6
  •   Larry K    14 年前

    回复:有没有一种方法可以让create()忽略模型中不存在的属性?--不,这是按设计的。

    你可以创建一个属性设置器 create ——

    attr_setter :a # will silently absorb additional parameter 'a' from the form.
    

    回复:或者,在创建新记录之前,删除不存在的属性的最佳方法是什么?

    您可以显式删除它们:

    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
    
        5
  •  3
  •   CambridgeMike    10 年前

    我提出了一个类似这样的解决方案,您可能会发现它很有用:

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

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

        6
  •  1
  •   afournier    13 年前

    我想用 可访问的 foo的模型类中的方法将实现您想要的,例如:

    class Foo < ActiveRecord::Base
    
      attr_accessible :a, :b, :c
    
      ...
    end
    

    这将只允许设置/更新列出的属性 可访问的 .

        7
  •  0
  •   originalhat    9 年前

    我发现了一个相当有效的解决方案,它最终是上述选项的组合。它允许传递(和忽略)无效参数,而有效参数则正确映射到对象。

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

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

        8
  •  0
  •   ironsand    8 年前

    你可以使用 Hash#slice column_names 方法也作为类方法存在。

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