April 28, 2007
Export Rails ActiveRecords to CSV
For a recent “enterprisey” project I’m working on, we had to offer a variety of CSV exports for many of the models in our system. Ruby’s FasterCSV library is great for raw parsing and generation of CSV data, so I used that as the basis for a quick and dirty system to easily provide customizable exports.
The main features are:
- Transform an array of exportable records into a whole CSV file. (By default, FasterCSV transforms arrays into a single CSV row.)
- Export a whole table my calling
.to_csvon the ActiveRecord subclass. - By default, export all of an ActiveRecord’s columns except for created_at and updated_at.
- Allow simple customization of exportable columns by overriding the
export_columnsmethod in your ActiveRecord class. - Allow multiple CSV formats by conditionally branching inside the
export_columnsmethod depending on the format parameter. - Allow complete customization of the export by overriding the
to_rowmethod. (I haven’t actually needed this much customization yet.)
The simplest example:
Address.to_csv |
Customizing the columns included in the CSV:
1 2 3 4 5 |
class Address < ActiveRecord::Base def export_columns(format = nil) %w[city state postal_code] end end |
Multiple output formats:
1 2 3 4 5 6 7 8 9 10 |
class Address < ActiveRecord::Base def export_columns(format = nil) case format when :local %w[street1 street2 city state postal_code] else %w[city state postal_code] end end end |
Here’s the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
require "fastercsv" class ActiveRecord::Base def self.to_csv(*args) find(:all).to_csv(*args) end def export_columns(format = nil) self.class.content_columns.map(&:name) - ['created_at', 'updated_at'] end def to_row(format = nil) export_columns(format).map { |c| self.send(c) } end end class Array def to_csv(options = {}) if all? { |e| e.respond_to?(:to_row) } header_row = first.export_columns(options[:format]).to_csv content_rows = map { |e| e.to_row(options[:format]) }.map(&:to_csv) ([header_row] + content_rows).join else FasterCSV.generate_line(self, options) end end end |
May 14, 2007 at 11:37 AM
Where in your Rails project do you put the ActiveRecord::Base and Array definitions? And do you put these in the same file, or in separate active_record.rb and array.rb files?
TIA…
May 18, 2007 at 12:42 PM
John—I put all of the code (for both AR::Base and Array) in a file in lib/exportable.rb
June 29, 2007 at 10:57 AM
Uhm, is it just the day, or why does the code refuse to load when put in lib.
printed the config.load_paths and lib wasn’t there.
added {RAILS_ROOT}/lib
still no Array.to_csv
hmm
July 22, 2007 at 4:52 PM
This is interesting. We actually use a SQL query (SELECT … INTO OUTFILE) for our csv exports. Since the data is already there in database, we like to keep rails mostly away from it.