2008-09-18 7 views
52

J'ai une table de base de données simple appelé « Entrées »:dans des rails, comment retourner les enregistrements sous forme de fichier 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 

Comment puis-je écrire un gestionnaire qui retourne le contenu de la table des entrées au format CSV fichier (idéalement d'une manière qu'il ouvrira automatiquement dans Excel)?

class EntriesController < ApplicationController 

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

    # ??? NOW WHAT ???? 

    end 

end 
+0

Au moins dans les versions les plus récentes de Rails, vous pouvez également utiliser 'entrée. tout à la place. –

Répondre

22

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

+29

Notez que ruby> = 1.9, FasterCSV est maintenant la bibliothèque CSV standard, et est juste appelée CSV. – kdt

+0

la clé étant 'require 'csv'', voir https://ruby-doc.org/stdlib/libdoc/csv/rdoc/CSV.html – mb21

7

Regardez la gemme FasterCSV.

Si tout ce dont vous avez besoin est un support Excel, vous pouvez aussi chercher à générer un xls directement. (Voir la feuille de calcul :: Excel)

gem install fastercsv 
gem install spreadsheet-excel 

Je trouve ces bonnes options 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:

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

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 afin 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: attachement; filename = "# {suggested_name} .xls"

Je suggère d'utiliser la grey rubis de fastercsv pour générer votre CSV, mais il y a aussi un csv intégré. L'exemple de code de fastercsv (de la documentation de la pierre) ressemble à ceci:

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

Merci pour cet obscur Content-Type! Je ne suis pas vraiment sûr que ce sera Excel à la fin, mais c'est bon à savoir. – Eric

87

FasterCSV est certainement le chemin à parcourir, mais si vous voulez servir directement à partir de votre application Rails, vous aurez envie de mettre en place une les en-têtes de réponse, aussi.

-je conserver une méthode autour de mettre en place le nom du 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 

L'utilisation qui le rend 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 une vue qui ressemble à ceci: (generate_csv est 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 %> 
+0

Hmm, je pensais que j'avais terminé jusqu'à ce que je vois ta réponse! Merci pour les détails de l'en-tête, je m'en souviendrai au cas où j'aurais des ennuis avec ce que j'utilise jusqu'ici. – Eric

+8

Comme mentionné ci-dessus, FasterCSV est juste CSV dans ruby ​​1.9 et plus. La méthode gererate_csv est maintenant CSV.generate. –

+4

Ajouter .html_safe à la fin du bloc generate_csv/CSV.generate fera en sorte que toutes les virgules dans les données soient correctement gérées. Sans cet appel mon fichier csv avait un tas de " " dedans. – stcorbett

24

J'ai accepté (et voté!) @ La réponse de Brian, pour m'avoir d'abord indiqué FasterCSV. Puis, quand j'ai googlé pour trouver la gemme, j'ai également trouvé un exemple assez complet au this wiki page. En les réunissant, j'ai choisi le code suivant.

Par ailleurs, la commande pour installer la pierre précieuse est: sudo gem install fastercsv (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 
+5

Je suggérerais de déplacer la génération CSV dans une vue: c'est plus de logique de présentation que vous voudriez normalement dans un contrôleur. –

+0

Vous pouvez définir le type sur 'text/csv'. Sinon, safari l'enregistrera en tant que 'entries.csv.txt' – silasjmatson

24

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

Exiger le csv de rubis bibliothèque dans un fichier initialiseur comme config/initializers/dependencies.rb

require "csv" 

comme certains arrière-plan 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 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 mettre en forme 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 se présente comme 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 du rapport que j'ai un lien pour exporter le rapport que l'utilisateur consulte. Ce qui suit est le link_to qui retourne la version csv du rapport:

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

N'oubliez pas d'utiliser '<% = CSV.generate_line (row) .html_safe%>' si vous utilisez les rails 3, pour éviter les caractères d'échappement. – dombesz

+6

Cette solution fonctionne correctement, mais j'ai dû modifier le paramètre des appels CSV.generate_line: row_sep à nil. Cette modification supprime les lignes vides indésirables de la réponse. Code: '<% = Ligne CSV.generate_line, {: row_sep => nil}%>' –

+2

Pour ajouter au commentaire de Wilson, Heroku (pile cèdre -beta) insère des lignes vides dans les fichiers csv sauf si vous lui dites explicitement de ne pas le faire. avec ': row_sep => nil'. – nslocum

2

Voici approché bien travaillé pour mon cas et provoque le navigateur pour 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 
1

essayer un bijou bien pour générer CSV de Rails https://github.com/crafterm/comma

+1

Bonne réponse mais pour le rendre génial, peut-être pourriez-vous expliquer pourquoi cette gemme (par opposition aux autres) –

0

Jetez un oeil à la gemme CSV Shaper.

https://github.com/paulspringett/csv_shaper

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

0

Si vous êtes tout simplement vouloir obtenir la base de données csv-vous de 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(""))} 
Questions connexes