How to generate CSV files in Rails 12

Posted by sbecker Thu, 07 Jun 2007 23:21:00 GMT

A while back someone posted on rubyonrails-talk asking how to export to CSV from Rails. I posted a solution, and people seemed to dig it, so I’ll share it again here.

Use FasterCSV

Get the FasterCSV gem. Why? It’s faster, and easier to use. Once you’ve got it, require it in environment.rb. Here’s an abbreviated version of my working controller method. Copy/paste/modify. And you’re done!


def export_to_csv
  @users = User.find(:all)

  csv_string = FasterCSV.generate do |csv|
    # header row
    csv << ["id", "first_name", "last_name"]

    # data rows
    @users.each do |user|
      csv << [user.id, user.first_name, user.last_name]
    end
  end

  # send it to the browsah
  send_data csv_string,
            :type => 'text/csv; charset=iso-8859-1; header=present',
            :disposition => "attachment; filename=users.csv"
end

HTML or CSV, have it your way

Now, if we wanted to get all clever about it, we could go further and serve both html and CSV data with only one action, using respond_to. This also requires that you add a RESTful route (map.resources :users) to your routes.rb file:

def index
  @users = User.find(:all)

  respond_to do |wants|
    wants.html
    wants.csv do
      csv_string = FasterCSV.generate do |csv|
        # header row
        csv << ["id", "first_name", "last_name"]

        # data rows
        @users.each do |user|
          csv << [user.id, user.first_name, user.last_name]
        end
      end

      # send it to the browsah
      send_data csv_string,
                :type => 'text/csv; charset=iso-8859-1; header=present',
                :disposition => "attachment; filename=users.csv"
    end
  end

end

Now if the user requests:

/users 

she’ll get HTML. If she requests:

/users.csv

You get the point.

Comments

Leave a response

  1. Avatar
    Jason Perry Fri, 08 Jun 2007 03:11:25 GMT

    I’ve got a little ruby class that encapsulates to dumping of csv files out to disk. I use this to generate some regular reports.

    Report.create "District Participation" do
      data = District.find(:all, :order => "number").map do |district|
        profiles = district.profiles
        ratings  = profiles.collect { |p| p.ratings.size }
        [district.county, profiles.size, ratings.sum]
      end
      [["District", "Respondants", "Ratings"]] + data
    end
    
    Report.create "Subject Area Participation" do
      data = Subject.find(:all, :order => "area").map do |subject|
        profiles = subject.profiles
        ratings  = profiles.collect { |p| p.ratings.size }
        [subject.area, profiles.size, ratings.sum]
      end
      [["Subject Area", "Respondants", "Ratings"]] + data
    end

    It needs somework, to clean up the way reports are defined, but it works well.

  2. Avatar
    Meekish Fri, 08 Jun 2007 03:51:39 GMT

    That’s very nice.

    How about making UsersController#index respond_to .csv instead of creating an entire action for it?

  3. Avatar
    Scott Becker Fri, 08 Jun 2007 03:57:38 GMT

    @Meekish – Good idea. That would be more Rails 2.0ish, wouldn’t it. Maybe I will post another example using respond_to soon.

  4. Avatar
    Scott Becker Fri, 08 Jun 2007 04:45:04 GMT

    @Meekish – Updated with a respond_to example.

  5. Avatar
    Meekish Fri, 08 Jun 2007 14:32:09 GMT

    Now that’s hot :)

  6. Avatar
    Gregory Brown Wed, 27 Jun 2007 14:27:50 GMT

    Using Ruport:

    class User < ActiveRecord::Base
      acts_as_reportable :only => ["id, "first_name", "last_name"]
    end
    
    # in controller
    def export_to_csv
       User.report_table.to_csv
    end
    
    

    Of course, maybe overkill if that’s all you need, but Ruport + acts_as_reportable also handles all associations and whatnot. (And uses FCSV under the hood)

  7. Avatar
    Joel Chippindale Tue, 05 Aug 2008 05:35:58 GMT

    Thanks Scott.

    I’ve made this into a simple CSV Builder plugin to make this even easier.

  8. Avatar
    Renne Tue, 16 Sep 2008 08:54:34 GMT

    Nice tool Joel. Very useful, thanks.

  9. Avatar
    curlysue282002@yahoo.com Fri, 12 Dec 2008 23:10:33 GMT

    Would this work for those who must use version Rails 0.14.1? Also how would I call this in my view

  10. Avatar
    Tim Wed, 18 Mar 2009 22:38:28 GMT

    thanks a lot. exactly what i needed, written clean and easy to copy. cheers, tim

  11. Avatar
    Kevin Thu, 03 Sep 2009 23:17:52 GMT

    Though this is a bit old it helped me get the basis for my time sheet going. This was very helpful, thanks for posting it.

  12. Avatar
    Kevin Thu, 03 Sep 2009 23:22:33 GMT

    This was very helpful thank you for posting it. I now have a csv I can use to create a time sheet. :)