【问题标题】:in rails, how to return records as a csv file在 Rails 中,如何将记录作为 csv 文件返回
【发布时间】:2010-09-10 19:19:28
【问题描述】:

我有一个名为“Entries”的简单数据库表:

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

如何编写一个处理程序,将 Entries 表的内容作为 CSV 文件返回(最好是在 Excel 中自动打开)?

class EntriesController < ApplicationController

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

    # ??? NOW WHAT ????

  end

end

【问题讨论】:

  • 至少在较新的 Rails 版本中,您还可以使用 Entry.all 代替。

标签: ruby-on-rails csv


【解决方案1】:

如果您只是想自己从控制台获取 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(""))}

【讨论】:

    【解决方案2】:

    看看 CSV Shaper gem。

    https://github.com/paulspringett/csv_shaper

    它有一个很好的 DSL 并且非常适合 Rails 模型。它还处理响应标头并允许自定义文件名。

    【讨论】:

      【解决方案3】:

      尝试一个不错的 gem 从 Rails 生成 CSV https://github.com/crafterm/comma

      【讨论】:

      • 很好的答案,但为了让它变得更好,也许你可以解释为什么这个宝石(而不是其他宝石)
      【解决方案4】:

      以下方法适用于我的案例,并导致浏览器在下载后打开适用于 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
      

      【讨论】:

        【解决方案5】:

        不使用 FasterCSV 的另一种方法:

        在 config/initializers/dependencies.rb 等初始化文件中需要 ruby​​ 的 csv 库

        require "csv"
        

        作为一些背景,以下代码基于创建搜索资源的Ryan Bate's Advanced Search Form。在我的例子中,搜索资源的 show 方法将返回之前保存的搜索结果。它还响应 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) %>
        

        【讨论】:

        • 如果您使用的是 rails 3,请不要忘记使用 &lt;%= CSV.generate_line(row).html_safe %&gt;,以避免转义字符。
        • 此解决方案运行良好,但我不得不将 CSV.generate_line 调用设置 :row_sep 更改为 nil。此更改会从响应中删除不需要的空行。代码:&lt;%= CSV.generate_line row, {:row_sep =&gt; nil} %&gt;
        • 添加到 Wilson 的评论中,Heroku (cedar stack -beta) 将在 csv 文件中插入空行,除非您明确告诉它不要使用 :row_sep =&gt; nil
        • 我还必须在 generate_line_headers 的末尾添加一个“-”。否则标题行后面有一个空行。
        【解决方案6】:

        我接受(并投了赞成票!)@Brian 的回答,因为我首先将我指向 FasterCSV。然后当我用谷歌搜索找到 gem 时,我还在 this wiki page 找到了一个相当完整的示例。将它们放在一起,我确定了以下代码。

        顺便说一下,安装gem的命令是: sudo gem install 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
        

        【讨论】:

        • 我建议将 CSV 生成移到视图中:这比您通常在控制器中想要的更多表示逻辑。
        • 您可能希望将类型设置为“文本/csv”。否则,safari 会将其保存为 'entries.csv.txt'
        【解决方案7】:

        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 %>
        

        【讨论】:

        • 嗯,我以为我已经完成了,直到我看到你的答案!感谢您提供标题详细信息,我会记住这些信息,以防我目前使用的内容遇到问题。
        • 如上所述,FasterCSV 只是 ruby​​ 1.9 及更高版本中的 CSV。 gererate_csv 方法现在是 CSV.generate。
        • 在 generate_csv/CSV.generate 块的末尾添加 .html_safe 将使其正确处理数据中的任何逗号。如果没有这个调用,我的 csv 文件有一堆“”;在里面。
        • 我发现自己一遍又一遍地提到这个答案,所以我终于硬着头皮做了一个 Rails 3 gem,它提供了此处提供的 render_csv 方法的略微修改版本:github.com/spindance/rendered_csv
        【解决方案8】:

        您需要在响应中设置 Content-Type 标头,然后发送数据。 Content_Type: application/vnd.ms-excel 应该可以解决问题。

        您可能还想设置 Content-Disposition 标头,使其看起来像 Excel 文档,并且浏览器会选择一个合理的默认文件名;这类似于 Content-Disposition: attachment; filename="#{suggested_name}.xls"

        我建议使用 fastercsv ruby​​ gem 来生成您的 CSV,但也有一个内置的 csv。 fastcsv 示例代码(来自 gem 的文档)如下所示:

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

        【讨论】:

        • 感谢那个晦涩的内容类型!我实际上不确定最终是否会成为 Excel,但很高兴知道。
        【解决方案9】:

        查看FasterCSV gem。

        如果您只需要 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
        

        【讨论】:

          【解决方案10】:

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

          【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-02-02
          • 2018-01-03
          • 2022-11-16
          • 2019-10-15
          • 2016-05-13
          • 1970-01-01
          • 2020-05-17
          相关资源
          最近更新 更多