Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: Ignoring non-existant attributes passed to create()

I have the following Rails model:

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

If I try and create a new record with an additional non-existent attribute, this produces an error:

Foo.create(a: 'some', b: 'string', c: 'foo', d: 'bar')
ActiveRecord::UnknownAttributeError: unknown attribute: d

Is there a way I can get create() to ignore attributes that don't exist in the model? Alternatively, what is the best way to remove non-existent attributes prior to creating the new record?

Many thanks

like image 504
gjb Avatar asked Nov 08 '10 21:11

gjb


5 Answers

Trying to think of a potentially more efficient way, but for now:

hash = { :a => 'some', :b => 'string', :c => 'foo', :d => 'bar' }
@something = Something.new
@something.attributes = hash.reject{|k,v| [email protected]?(k.to_s) }
@something.save
like image 131
jenjenut233 Avatar answered Nov 11 '22 15:11

jenjenut233


I use this frequently (simplified):

params.select!{|x| Model.attribute_names.index(x)}
Model.update_attributes(params)
like image 22
user1919149 Avatar answered Nov 11 '22 14:11

user1919149


I just had this exact problem upgrading to Rails 3.2, when I set:

config.active_record.mass_assignment_sanitizer = :strict

It caused some of my create! calls to fail, since fields that were previously ignored are now causing mass assignment errors. I worked around it by faking the fields in the model as follows:

attr_accessor   :field_to_exclude
attr_accessible :field_to_exclude
like image 7
Amir Rubin Avatar answered Nov 11 '22 13:11

Amir Rubin


Re: Is there a way I can get create() to ignore attributes that don't exist in the model? -- No, and this is by design.

You can create an attr_setter that will be used by create --

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

Re: Alternatively, what is the best way to remove non-existent attributes prior to creating the new record?

You can remove them explicitly:

params[:Foo].delete(:a) # delete the extra param :a

But the best is to not put them there in the first place. Modify your form to omit them.

Added:

Given the updated info (incoming data), I think I'd create a new hash:

incoming_data_array.each{|rec|
  Foo.create {:a => rec['a'], :b => rec['b'], :c => rec['c']} # create new
                                                              # rec from specific
                                                              # fields
}

Added more

# 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
like image 6
Larry K Avatar answered Nov 11 '22 14:11

Larry K


I came up with a solution that looks like this, you might find it helpful:

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

This was an ideal solution for me, because in my case hash was coming from an ideal data source which mirrored my schema (except there were additional fields).

like image 4
CambridgeMike Avatar answered Nov 11 '22 15:11

CambridgeMike