web-dev-qa-db-fra.com

dans Rails, comment retourner des enregistrements sous forme de fichier csv

J'ai une table de base de données simple appelée "Entrées":

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

Comment puis-je écrire un gestionnaire qui renverra le contenu de la table des entrées sous forme de fichier CSV (idéalement de manière à ce qu'il s'ouvre automatiquement dans Excel)?

class EntriesController < ApplicationController

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

    # ??? NOW WHAT ????

  end

end
55
Eric

Il existe un plugin appelé FasterCSV qui gère cela à merveille.

23
Brian

FasterCSV est certainement le chemin à parcourir, mais si vous souhaitez le servir directement à partir de votre application Rails, vous voudrez également configurer des en-têtes de réponse.

Je garde une méthode pour configurer le nom de fichier et les en-têtes nécessaires:

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

En utilisant cela, il est facile d'avoir quelque chose comme ça dans mon contrôleur:

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

Et avoir une vue qui ressemble à ceci: (generate_csv vient 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 %>
88

J'ai accepté (et voté!) @ La réponse de Brian, pour m'avoir d'abord pointé sur FasterCSV. Ensuite, lorsque j'ai cherché sur Google pour trouver la gemme, j'ai également trouvé un exemple assez complet sur cette page wiki . En les assemblant, je me suis installé sur le code suivant.

Par ailleurs, la commande pour installer la gemme est: Sudo gem install fastcsv (tout en minuscules)

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
25
Eric

Une autre façon de le faire sans utiliser FasterCSV:

Requiert la bibliothèque csv de Ruby dans un fichier d'initialisation comme config/initializers/dependencies.rb

require "csv"

Comme arrière-plan, le code suivant est basé sur Formulaire de recherche avancée de Ryan Bate qui crée une ressource de recherche. Dans mon cas, la méthode show de la ressource de recherche retournera les résultats d'une recherche précédemment enregistrée. Il répond également à csv et utilise un modèle de vue pour formater la sortie souhaitée.

  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

Le fichier show.csv.erb ressemble à ceci:

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

Sur la version html de la page du rapport, j'ai un lien pour exporter le rapport que l'utilisateur consulte. Voici le link_to qui renvoie la version csv du rapport:

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

Jetez un œil à la gemme FasterCSV .

Si vous n'avez besoin que de la prise en charge d'Excel, vous pouvez également envisager de générer directement un xls. (Voir Feuille de calcul :: Excel)

gem install fastercsv
gem install spreadsheet-Excel

Je trouve ces options bonnes pour ouvrir le fichier csv dans Windows Excel:

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

Quant à la partie ActiveRecord, quelque chose comme ça ferait:

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

L'approche suivante a bien fonctionné pour mon cas et amène le navigateur à ouvrir l'application appropriée pour le type CSV après le téléchargement.

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
2
rantler

Vous devez définir l'en-tête Content-Type dans votre réponse, puis envoyer les données. Content_Type: application/vnd.ms-Excel devrait faire l'affaire.

Vous pouvez également définir l'en-tête Content-Disposition de sorte qu'il ressemble à un document Excel et que le navigateur sélectionne un nom de fichier par défaut raisonnable; c'est quelque chose comme Content-Disposition: pièce jointe; filename = "# {suggest_name} .xls"

Je suggère d'utiliser le rapidcsv Ruby gem pour générer votre CSV, mais il y a aussi un csv intégré. L'exemple de code accelercsv (de la documentation du gem) ressemble à ceci:

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

essayez un joli bijou pour générer du CSV à partir de Rails https://github.com/crafterm/comma

1
Henry Jacob

Jetez un oeil à la gemme CSV Shaper.

https://github.com/paulspringett/csv_shaper

Il a une belle DSL et fonctionne très bien avec les modèles Rails. Il gère également les en-têtes de réponse et permet la personnalisation du nom de fichier.

0
paul

Si vous voulez simplement obtenir la base de données csv vous-même depuis la console, vous pouvez le faire en quelques lignes

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