justalisteningman
justalisteningman

Reputation: 27

Why am I getting a Mass Assignment Error when I'm using Attr_Accessable?

I'm trying to write the ability for the admin to upload a CSV full of Judges. I'm getting an AssignmentError when I try and do this. This is really confusing to me, as I've used attr_accessible on all the relevant variables. All the research I've done suggests that if you use attr_accessible, you should be able to mass assign variables.

I'm still a rails newbie, so excuse me if I'm doing something really dumb.

Test CSV:

Name,Email,Competition_ID,Password
Brandon Schultz,[email protected],1,12345678
Mr. Foo,[email protected],1,12345678
Mr. Bar,[email protected],1,12345678

Error Message:

ActiveModel::MassAssignmentSecurity::Error in JudgesController#import
Can't mass-assign protected attributes: Name, Email, Competition_ID, Password

Application Trace:

app/models/judge.rb:17:in `block in import'
app/models/judge.rb:16:in `import'
app/controllers/judges_controller.rb:32:in `import'

Here's the relevant model:

class Judge < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
     :recoverable, :rememberable, :trackable, :validatable

  attr_accessible :email, :password, :password_confirmation, :remember_me, :name, :competition_id

  belongs_to :competition

def self.import(file)
    CSV.foreach(file.path, headers: true) do |row|
            Judge.create! row.to_hash
        end
    end
end

Relevent Controller:

def import
  Judge.import(params[:file])
  redirect_to admin_portal_path, notice: "Products imported."
end

Upvotes: 1

Views: 131

Answers (1)

Josh Kovach
Josh Kovach

Reputation: 7749

The hash that row.to_hash gives you will look like this:

{ 'Name' => 'Mr. Foo', 'Email' => '[email protected]', ... }

The problem here is that the keys are strings that are capitalized. 'Email' != 'email'.

If you are passing the hash directly to create!, it needs to conform to the attribute names exactly, either as strings or symbols:

{ 'name' => 'Mr. Foo', :email => '[email protected]', ... }

You should sanitize the keys to be snake_case (the same as their respective accessible attributes) before passing them to create!, or change them in the CSV.

You might also consider assigning each attribute individually from the row hash, and assigning a default value if it a value is not found.

hash = row.to_hash
judge = Judge.new
judge.name = hash['Name'] || hash['name'] || 'No name'
judge.email = hash['Email'] || hash['email']
judge.competition_id = hash['Competition_ID'] || hash['competition_id']

This is probably not the best thing to do in this case, but it can make your import script be much more robust if you are not in strict control over the format of the CSV files. It also gives you the opportunity to sanitize the values before writing them into your database.

Upvotes: 2

Related Questions