Reputation: 135
trying to upload csv record into City model database, getting error
ActiveRecord::UnknownAttributeError in PagesController#import
unknown attribute 'country ' for City. (City.create! row.to_hash)
csv:
name country general_info1 general_info2 (etc)
Toronto Canada This is a test (nil)
upload view
<%= form_tag upload_path, multipart: true do %>
<%= file_field_tag :file %>
<%= submit_tag "import" %>
<% end %>
route:
post '/upload' => 'pages#import'
PagesController
def import
City.import(params[:file])
end
City model
def self.import(file)
logger.info "__________________"
logger.info file.inspect
logger.info file.path
logger.info "__________________"
CSV.foreach(file.path, headers: true) do |row|
City.create! row.to_hash
#puts '&&&&&&&&&&&&&&&&&&&&&&&' + row[1]
logger.info row.inspect
end
schema
ActiveRecord::Schema.define(version: 20160513090837) do
create_table "cities", force: :cascade do |t|
t.string "name"
t.string "country"
t.string "general_info1"
t.string "general_info2"
t.integer "happiness_rating"
t.integer "family_safety_rating"
t.string "family_safety_info"
t.integer "bike_hobby_rating"
t.string "bike_hobby_info"
t.integer "accountant_rating"
t.integer "accountant_shortage_rating"
t.string "accountant_shortage"
t.integer "accountant_avg_salary"
t.integer "graphic_designer_rating"
t.string "graphic_designer_shortage"
t.integer "graphic_designer_avg_salary"
t.integer "journalist_rating"
t.string "journalist_shortage"
t.integer "journalist_avg_salary"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
many thanks in advance
answer:
CSV.foreach(file.path, {headers: true, header_converters: :symbol}) do |row|
City.create!(row.to_hash)
end
Upvotes: 2
Views: 282
Reputation: 12514
First of, ActiveRecord::UnknownAttributeError
occurs if the field
that City
models's create!
method is expecting to be present in cities
table is absent. Make sure its there.
can somebody please explain why the header needs to be a symbol?
When you parse CSV in ruby, you normally get the data as collection of Hash
es. In the following example you might have noticed that the keys
to all the Hash
es are repeating and they are String
data. In Ruby
every String
instance occupies separate memory space.
csv.to_a.map {|row| row.to_hash }
# => [{"Year"=>"1997", "Make"=>"Ford", "Model"=>"E350", "Description"=>"ac, abs, moon", "Price"=>"3000.00"}, {"Year"=>"1999", "Make"=>"Chevy", "Model"=>"Venture \"Extended Edition\"", "Description"=>"", "Price"=>"4900.00"}, {"Year"=>"1999", "Make"=>"Chevy", "Model"=>"Venture \"Extended Edition, Very Large\"", "Description"=>nil, "Price"=>"5000.00"}, {"Year"=>"1996", "Make"=>"Jeep", "Model"=>"Grand Cherokee", "Description"=>"MUST SELL!\nair, moon roof, loaded", "Price"=>"4799.00"}]
Say string computer
occupies 8 bytes
. if I define this string 1000,000
times, it will occupy 8MB
of my RAM.
Now if you use header_converters: :symbol
, this will invoke to_sym
method which will now define the keys
as symbol
s.
csv = CSV.new(body, :headers => true, :header_converters => :symbol)
csv.to_a.map {|row| row.to_hash }
# => [{:year=>"1997", :make=>"Ford", :model=>"E350", :description=>"ac, abs, moon", :price=>"3000.00"}, {:year=>"1999", :make=>"Chevy", :model=>"Venture \"Extended Edition\"", :description=>"", :price=>"4900.00"}, {:year=>"1999", :make=>"Chevy", :model=>"Venture \"Extended Edition, Very Large\"", :description=>nil, :price=>"5000.00"}, {:year=>"1996", :make=>"Jeep", :model=>"Grand Cherokee", :description=>"MUST SELL!\nair, moon roof, loaded", :price=>"4799.00"}]
What is special about symbol?
Symbol is memory efficient and retrieval is very fast. if you define symbol :computer
1000,000
times it will only occupy 8Bytes
of memory.
How is this explanation addressing my question?
You CSV file might have thousands of lines of data to be converted to Hash
. So to make this process faster and memory efficient, you use header_converters: :symbol
See this for more info http://ruby-doc.org/stdlib-2.0.0/libdoc/csv/rdoc/CSV.html#HeaderConverters
Upvotes: 0