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,b@schultz.com,1,12345678
Mr. Foo,foo@foo.com,1,12345678
Mr. Bar,bar@bar.com,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
有帮助吗?

解决方案

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

{ 'Name' => 'Mr. Foo', 'Email' => 'foo@bar.com', ... }

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 => 'foo@bar.com', ... }

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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top