daveasdf
daveasdf

Reputation: 135

UnknownAttributeError csv

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

Answers (1)

Shiva
Shiva

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 Hashes. In the following example you might have noticed that the keys to all the Hashes 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 symbols.

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

Related Questions