2008-09-18 101 views
52

我有一个名为“项”的简单数据库表:,如何返回记录为CSV文件

class CreateEntries < ActiveRecord::Migration 
    def self.up 
    create_table :entries do |t| 
     t.string :firstName 
     t.string :lastName 
     #etc. 
     t.timestamps 
    end 
    end 

    def self.down 
    drop_table :entries 
    end 
end 

我如何写一个处理程序将返回条目表的内容为CSV文件(理想情况下它会自动在Excel中打开)?

class EntriesController < ApplicationController 

    def getcsv 
    @entries = Entry.find(:all) 

    # ??? NOW WHAT ???? 

    end 

end 
+0

至少在最近版本的Rails,你也可以用`进入这样做。全部`而不是。 – 2010-09-29 00:47:23

回答

22

有一个名为FasterCSV的插件可以很好地处理这个问题。

+29

请注意,ruby> = 1.9,FasterCSV现在是标准的CSV库,并且被称为CSV。 – kdt 2011-01-25 17:23:41

+0

关键是`require'csv'`,请参阅https://ruby-doc.org/stdlib/libdoc/csv/rdoc/CSV.html – mb21 2017-02-24 08:46:57

7

看看FasterCSV宝石。

如果您只需要支持excel,那么您可能还会考虑直接生成xls。 (见表格:: Excel)中

gem install fastercsv 
gem install spreadsheet-excel 

我发现这些选项适合在Windows Excel中打开CSV文件:

FasterCSV.generate(:col_sep => ";", :row_sep => "\r\n") { |csv| ... } 

至于ActiveRecord的部分,像这样的事:

CSV_FIELDS = %w[ title created_at etc ] 
FasterCSV.generate do |csv| 
    Entry.all.map { |r| CSV_FIELDS.map { |m| r.send m } }.each { |row| csv << row } 
end 
2

您需要在响应中设置Content-Type标头,然后发送数据。 Content_Type:应用程序/ vnd.ms-excel应该可以做到。

您可能还需要设置Content-Disposition标头,使其看起来像一个Excel文档,并且浏览器选择一个合理的默认文件名;这就像Content-Disposition:附件;文件名=“#{建议名} .xls”

我建议使用fastercsv ruby​​ gem来生成您的CSV,但也有一个内置的csv。该fastercsv示例代码(来自创业板的文档)看起来是这样的:

csv_string = FasterCSV.generate do |csv| 
    csv << ["row", "of", "CSV", "data"] 
    csv << ["another", "row"] 
# ... 
end 
+0

感谢那些晦涩难懂的Content-Type!我实际上并不确定它最终会不会是Excel,但这很好理解。 – Eric 2008-09-18 17:28:49

87

FasterCSV肯定是要走的路,但如果你想直接从您的Rails应用程序服务于它,你要设置一些响应标题也是如此。

我养的方法周围设置文件名和必要的标头:

def render_csv(filename = nil) 
    filename ||= params[:action] 
    filename += '.csv' 

    if request.env['HTTP_USER_AGENT'] =~ /msie/i 
    headers['Pragma'] = 'public' 
    headers["Content-type"] = "text/plain" 
    headers['Cache-Control'] = 'no-cache, must-revalidate, post-check=0, pre-check=0' 
    headers['Content-Disposition'] = "attachment; filename=\"#{filename}\"" 
    headers['Expires'] = "0" 
    else 
    headers["Content-Type"] ||= 'text/csv' 
    headers["Content-Disposition"] = "attachment; filename=\"#{filename}\"" 
    end 

    render :layout => false 
end 

使用,可以很容易有这样的事情在我的控制器:

respond_to do |wants| 
    wants.csv do 
    render_csv("users-#{Time.now.strftime("%Y%m%d")}") 
    end 
end 

而且有一个观点看起来像这样:(generate_csv来自FasterCSV)

UserID,Email,Password,ActivationURL,Messages 
<%= generate_csv do |csv| 
    @users.each do |user| 
    csv << [ user[:id], user[:email], user[:password], user[:url], user[:message] ] 
    end 
end %> 
+0

嗯,我以为我完成了,直到我看到你的答案!感谢您的头部细节,我会记住那些以防万一到目前为止我遇到麻烦。 – Eric 2008-09-18 17:27:36

+8

如上所述,FryCSV在ruby 1.9及更高版本中只是CSV。 gererate_csv方法现在是CSV.generate。 – 2011-08-10 01:24:14

+4

在generate_csv/CSV.generate块的末尾添加.html_safe将使其正确处理数据中的任何逗号。没有这个调用,我的csv文件里面有一堆" "。 – stcorbett 2011-10-10 20:07:03

24

我接受了(并投票赞成!)@布莱恩的回答,首先指向我的FasterCSV。然后,当我搜索到宝石时,我还在this wiki page找到了一个相当完整的例子。把它们放在一起,我解决了下面的代码。

顺便说一句,安装宝石的命令是: sudo的创业板安装fastercsv (全部小写)

require 'fastercsv' 

class EntriesController < ApplicationController 

    def getcsv 
     entries = Entry.find(:all) 
     csv_string = FasterCSV.generate do |csv| 
      csv << ["first","last"] 
      entries.each do |e| 
       csv << [e.firstName,e.lastName] 
      end 
      end 
      send_data csv_string, :type => "text/plain", 
      :filename=>"entries.csv", 
      :disposition => 'attachment' 

    end 


end 
24

另一种方式来做到这一点,而无需使用FasterCSV:

要求红宝石的CSV初始化文件中的库,如config/initializers/dependencies.rb

require "csv" 

由于某些背景下面的代码是基于创建搜索资源的Ryan Bate's Advanced Search Form。在我的情况下,搜索资源的显示方法将返回以前保存的搜索结果。它也响应csv,并使用视图模板来格式化所需的输出。

def show 
    @advertiser_search = AdvertiserSearch.find(params[:id]) 
    @advertisers = @advertiser_search.search(params[:page]) 
    respond_to do |format| 
     format.html # show.html.erb 
     format.csv # show.csv.erb 
    end 
    end 

的show.csv.erb文件如下所示:

<%- headers = ["Id", "Name", "Account Number", "Publisher", "Product Name", "Status"] -%> 
<%= CSV.generate_line headers %> 
<%- @advertiser_search.advertisers.each do |advertiser| -%> 
<%- advertiser.subscriptions.each do |subscription| -%> 
<%- row = [ advertiser.id, 
      advertiser.name, 
      advertiser.external_id, 
      advertiser.publisher.name, 
      publisher_product_name(subscription), 
      subscription.state ] -%> 
<%= CSV.generate_line row %> 
<%- end -%> 
<%- end -%> 

在报告页上我有一个导出链接,用户正在查看报告的HTML版本。以下是返回该报告的CSV版本的link_to:

<%= link_to "Export Report", formatted_advertiser_search_path(@advertiser_search, :csv) %> 
2

以下走近我的情况下工作良好,导致浏览器下载后打开的CSV类型相应的应用程序。

def index 
    respond_to do |format| 
    format.csv { return index_csv } 
    end 
end 

def index_csv 
    send_data(
    method_that_returns_csv_data(...), 
    :type => 'text/csv', 
    :filename => 'export.csv', 
    :disposition => 'attachment' 
) 
end 
0

如果你只是想自己从控制台获得CSV数据库,你可以在几行

tags = [Model.column_names] 
rows = tags + Model.all.map(&:attributes).map(&:to_a).map { |m| m.inject([]) { |data, pair| data << pair.last } } 
File.open("ss.csv", "w") {|f| f.write(rows.inject([]) { |csv, row| csv << CSV.generate_line(row) }.join(""))}