en Rails, cómo devolver registros como un archivo csv

Tengo una tabla de base de datos simple llamada “Entradas”:

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 

¿Cómo escribo un controlador que devolverá los contenidos de la tabla de entradas como un archivo CSV (idealmente de forma que se abra automáticamente en Excel)?

 class EntriesController < ApplicationController def getcsv @entries = Entry.find( :all ) # ??? NOW WHAT ???? end end 

Hay un complemento llamado FasterCSV que maneja esto maravillosamente.

FasterCSV es definitivamente el camino a seguir, pero si desea servirlo directamente desde su aplicación Rails, también querrá configurar algunos encabezados de respuesta.

Guardo un método para configurar el nombre de archivo y los encabezados necesarios:

 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 

Usar eso hace que sea fácil tener algo como esto en mi controlador:

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

Y tenga una vista que se vea así: ( generate_csv es de 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 %> 

Acepté (¡y voté!) La respuesta de @ Brian, por primera vez me indicó FasterCSV. Luego, cuando busqué en Google para encontrar la gem, también encontré un ejemplo bastante completo en esta página wiki . Poniéndolos juntos, me decidí por el siguiente código.

Por cierto, el comando para instalar la gem es: sudo gem install fastercsv (todo en minúsculas)

 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 

Otra forma de hacerlo sin usar FasterCSV:

Requerir la biblioteca csv de ruby ​​en un archivo de inicializador como config / initializers / dependencies.rb

 require "csv" 

Como antecedentes, el siguiente código se basa en el formulario de búsqueda avanzada de Ryan Bate que crea un recurso de búsqueda. En mi caso, el método show del recurso de búsqueda devolverá los resultados de una búsqueda previamente guardada. También responde a csv y usa una plantilla de vista para formatear el resultado deseado.

  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 

El archivo show.csv.erb tiene el siguiente aspecto:

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

En la versión html de la página del informe, tengo un enlace para exportar el informe que el usuario está viendo. El siguiente es el link_to que devuelve la versión csv del informe:

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

Echa un vistazo a la gem de FasterCSV .

Si todo lo que necesita es soporte de excel, también puede buscar generar un xls directamente. (Ver hoja de cálculo :: Excel)

 gem install fastercsv gem install spreadsheet-excel 

Encuentro estas opciones buenas para abrir el archivo csv en Windows Excel:

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

En cuanto a la parte ActiveRecord, algo como esto sería:

 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 

Debe configurar el encabezado Content-Type en su respuesta, luego envíe los datos. Content_Type: application / vnd.ms-excel debería hacer el truco.

Es posible que también desee configurar el encabezado Content-Disposition para que se vea como un documento de Excel, y el navegador elija un nombre de archivo predeterminado razonable; eso es algo como Content-Disposition: attachment; filename = “# {suggested_name} .xls”

Sugiero usar la gem ruby ​​fastercsv para generar su CSV, pero también hay una csv incorporada. El código de ejemplo de fastercsv (de la documentación de la gem) se ve así:

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

El siguiente enfoque funcionó bien para mi caso y hace que el navegador abra la aplicación apropiada para el tipo CSV después de la descarga.

 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 

prueba una buena joya para generar CSV desde Rails https://github.com/crafterm/comma

Echa un vistazo a la gem CSV Shaper.

https://github.com/paulspringett/csv_shaper

Tiene un buen DSL y funciona muy bien con los modelos de Rails. También maneja los encabezados de respuesta y permite la personalización del nombre de archivo.

Si simplemente desea obtener la base de datos csv desde la consola, puede hacerlo en unas pocas líneas

 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(""))}