2

Is anyone aware of any tutorials that demonstrate how to import data in a Ruby app with FasterCSV and saving it to a SQLite or MySQL database?

Here are the specific steps involved:

  1. Reading a file line by line (the .foreach method does this according to documentation)
  2. Mapping header names in file to database column names
  3. Creating entries in database for CSV data (seems doable with .new and .save within a .foreach block)

This is a basic usage scenario but I haven't been able to find any tutorials for it, so any resources would be helpful.

Thanks!

Jim Connors
  • 297
  • 4
  • 14
  • possible duplicate of [Best way to read CSV in Ruby. FasterCSV?](http://stackoverflow.com/questions/4369039/best-way-to-read-csv-in-ruby-fastercsv) – the Tin Man Jun 04 '11 at 18:27
  • 1
    Thanks, already saw that. Unfortunately it doesn't address steps 2 or 3 I've mentioned above. I also don't need just a tool to do a one-time import, but to build it into the app to let users import data. – Jim Connors Jun 04 '11 at 18:31
  • What code have you written so far? – the Tin Man Jun 04 '11 at 18:39

4 Answers4

2

So it looks like FasterCSV is now part of the Ruby core as of Ruby 1.9, so this is what I ended up doing, to achieve the goals in my question above:

@importedfile = Import.find(params[:id])
filename = @importedfile.csv.path
CSV.foreach(filename, {:headers => true}) do |row|
  @post = Post.find_or_create_by_email(
    :content          =>  row[0],
    :name             =>  row[1],
    :blog_url         =>  row[2],
    :email            =>  row[3]
  )
end
flash[:notice] = "New posts were successfully processed."
redirect_to posts_path

Inside the find_or_create_by_email function is the mapping from the database columns to the columns of the CSV file: row[0], row[1], row[2], row[3].

Since it is a find_or_create function I don't need to explicitly call @post.save to save the entry to the database.

If there's a better way please update or add your own answer.

Jim Connors
  • 297
  • 4
  • 14
0

This seems to be the shortest way, if you can use the ID to identify the records and if no mapping of column names is necessary:

CSV.foreach(filename, {:headers => true}) do |row|
  post = Post.find_or_create_by_id row["id"]
  post.update_attributes row.to_hash
end
0x4a6f4672
  • 27,297
  • 17
  • 103
  • 140
0

First, start with other Stack Overflow answers: Best way to read CSV in Ruby. FasterCSV?

Before jumping into writing the code, I check whether there is an existing tool to do the import. You might want to look at mysqlimport.

This is a simple example showing how to map the CSV headers to a database's columns:

require "csv"

data = <<EOT
header1, header2, header 3
1, 2, 3
2, 2, 3
3, 2, 3
EOT

header_to_table_columns = {
  'header1'  => 'col1',
  'header2'  => 'col2',
  'header 3' => 'col3'
}

arr_of_arrs = CSV.parse(data)
headers = arr_of_arrs.shift.map{ |i| i.strip }
db_cols = header_to_table_columns.values_at(*headers)
arr_of_arrs.each do |ary|
  # insert into the database using an ORM or by creating insert statements
end
Community
  • 1
  • 1
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
0

Ruby is great for rolling your own import routines.

  1. Reading a file(handy block structure to ensure that the file handle is closed properly):

    File.open( filepath ) do |f|
      f.each_line do |line|
        do something with the line...
      end
    end
    
  2. Mapping header names to columns(you might want to check for matching array lengths):

    Hash[header_array.zip( line_array )]
    
  3. Creating entries in the database using activerecord:

    SomeModel.create( Hash[header_array.zip( line_array )] )
    

It sounds like you are planning to let users upload csv files and import them into the database. This is asking for trouble unless they are savvy about data. You might want to look into a nosql solution to simplify things on the import front.

seph
  • 6,066
  • 3
  • 21
  • 19
  • I'll have to give this a shot. You're right, it is asking for trouble with at-large users, but my users are few and are developers themselves, so it's designed with that in mind. – Jim Connors Jun 05 '11 at 17:29