55 votes

En rails, comment retourner des enregistrements sous forme de fichier csv

J'ai une table de base de données simple appelée "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

Comment puis-je écrire un gestionnaire qui retournera le contenu de la table Entries 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 )

    # ??? MAINTENANT QUOI ????

  end

end

89voto

Clinton R. Nixon Points 4283

FasterCSV est définitivement la voie à suivre, mais si vous souhaitez le servir directement depuis votre application Rails, vous voudrez également configurer quelques 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 ceci dans mon contrôleur :

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

Et avoir une vue qui ressemble à ceci : (generate_csv est de FasterCSV)

IDUtilisateur,Email,Mot de passe,URLActivation,Messages
<%= generate_csv do |csv|
  @users.each do |user|
    csv << [ user[:id], user[:email], user[:password], user[:url], user[:message] ]
  end
end %>

26voto

Eric Points 5994

J'ai accepté (et voté pour!) la réponse de @Brian, car il m'a d'abord dirigé vers FasterCSV. Ensuite, lorsque j'ai googlé pour trouver le gem, j'ai également trouvé un exemple assez complet sur cette page wiki. En les mettant ensemble, j'ai opté pour le code suivant.

Au fait, la commande pour installer le gem est : sudo gem install fastercsv (tout en minuscules)

require 'fastercsv'

class EntriesController < ApplicationController

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

  end

end

25voto

rwc9u Points 532

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

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

require "csv"

En guise de contexte, le code suivant est basé sur Ryan Bate's Advanced Search Form qui crée une ressource de recherche. Dans mon cas, la méthode show de la ressource de recherche renverra les résultats d'une recherche précédemment enregistrée. Il répond également au format csv et utilise un modèle de vue pour formater la sortie désiré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 à ce qui suit :

<%- 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 de rapport, j'ai un lien pour exporter le rapport que l'utilisateur est en train de consulter. Voici le lien link_to qui renvoie la version csv du rapport :

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

23voto

Brian Points 3889

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

7voto

kch Points 25855

Jetez un coup d'œil à la gem FasterCSV.

Si tout ce dont vous avez besoin est le support Excel, vous pourriez également envisager de générer un fichier xls directement. (Voir Spreadsheet::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 ceci ferait l'affaire :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X