# CSV Download of any given model

**URL:** <https://www.ruby-forum.com/t/csv-download-of-any-given-model/106984>\
**Category:** Rails\
**Created:** [June 16, 2007, 3:25am UTC](https://www.ruby-forum.com/t/csv-download-of-any-given-model/106984 "2007-06-16T03:25:36Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [June 16, 2007, 3:25am UTC](https://www.ruby-forum.com/t/csv-download-of-any-given-model/106984/1 "2007-06-16T03:25:36Z")

</div>

I found sample code from the archives of this list to make CSV  
download of a model. It works fine. I am wondering if it is possible  
to generalize the code posted by François on his blog to handle any  
given model.

class ReportController \< ApplicationController  
def report  
@models = Model.find(:all, :conditions =\> [’…’])  
report = StringIO.new  
CSV::Writer.generate(report, ‘,’) do |csv|  
csv \<\< %w(Title Total)  
@models.each do |model|  
csv \<\< [model.title, model.total]  
end  
end

report.rewind  
send\_data(report.read,  
:type =\> ‘text/csv; charset=iso-8859-1; header=present’,  
:filename =\> ‘report.csv’)  
end  
end

TIA.

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [July 26, 2007, 7:28am UTC](https://www.ruby-forum.com/t/csv-download-of-any-given-model/106984/2 "2007-07-26T07:28:55Z")

</div>

I’m curious on this as well… my code is a little different then that  
of the blog post referenced:

class SignaturesController \< ApplicationController  
def export  
require ‘csv’  
content\_type = if request.user\_agent =~ /windows/i  
‘application/vnd.ms-excel’  
else  
‘text/csv’  
end

```
CSV::Writer.generate(output = "") do |csv|
  csv << Signature.column_names
  Signature.find(:all).each do |signature|
    csv << [signature.id, signature.firstname, signature.lastname]
  end
end
send_data(output, :type => content_type, :filename =>

```

“#{controller\_name}-export.csv”)  
end  
end

I’m already using Model.column\_names to get the header of the CSV  
file. Is there a way to loop through Model.content\_columns and grab  
the name variable to include within the block rather then specifying  
the column individually?

Thoughts from anyone? Thanks…

Tim

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [July 27, 2007, 7:09pm UTC](https://www.ruby-forum.com/t/csv-download-of-any-given-model/106984/3 "2007-07-27T19:09:23Z")

</div>

I think you’ll find this simpler and more flexible. First, you’ll  
need to download the FasterCSV gem (‘gem install fastercsv’). Then  
you can use:

class FasterCSVExport  
require ‘fastercsv’

def create\_report( field\_names = [], data\_rows = [] )  
FasterCSV.generate do |csv|  
csv \<\< field\_names.map {|fn| fn.humanize.titleize }  
data\_rows.each {|row| csv \<\< row }  
end  
end  
end

I’ve given it to you as a stand-alone class but the create\_report  
method can also be added to each model directly or as part of a module  
that gets mixed in (which is how I do it). Then in your controller  
you can just have:

field\_names = [‘id’, ‘firstname’, ‘lastname’]  
table\_rows = YourModel.find(:all).collect {|item| [item.id,  
item.firstname, item.lastname]  
export\_data = FasterCSVExport.create\_report(field\_names, table\_rows)  
send\_data(export\_data, :type =\> “text/csv; charset=utf-8;  
header=present”,  
:disposition =\> ‘attachment’, :filename =\> “exported\_data.csv”)

Even easier, you can pass your field\_names to the “select” option when  
performing the find to quickly have both the table header and the  
table rows contain the same attributes. Something like:

field\_names = [‘id’, ‘firstname’, ‘lastname’]  
table\_rows = YourModel.find(:all, :select =\> field\_names)  
export\_data = FasterCSVExport.create\_report(field\_names, table\_rows)  
send\_data(export\_data, :type =\> “text/csv; charset=utf-8;  
header=present”,  
:disposition =\> ‘attachment’, :filename =\> “exported\_data.csv”)

Or if you opt to put the ‘create\_report’ method into your model  
directly, you can push the find to the model too and let it handle  
both steps at once.

export\_data = YourModel.create\_report([‘id’, ‘firstname’, ‘lastname’])  
send\_data(export\_data, :type =\> “text/csv; charset=utf-8;  
header=present”,  
:disposition =\> ‘attachment’, :filename =\> “exported\_data.csv”)

> From there you can start to add bells and whistles. Tim, something  
> like:  
> field\_names = YourModel.column\_names if field\_names.blank?  
> might be what you want to grab all the column names.

(I’m typing this from scratch without testing it so it’s quite  
possible I’ve typo’d something…)

HTH,  
Kevin S.

[http://www.nullislove.com](http://www.nullislove.com)

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [July 29, 2007, 8:51pm UTC](https://www.ruby-forum.com/t/csv-download-of-any-given-model/106984/4 "2007-07-29T20:51:01Z")

</div>

Thank you Kevin - that makes sense, I’ll check it out when I get back  
to the office tomorrow.

Regards,

Tim
