Reputation: 27
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
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