222 votes

Ruby on Rails - Importation de données depuis un fichier CSV

Je voudrais importer des données d'un fichier CSV dans une table de base de données existante. Je ne veux pas sauvegarder le fichier CSV, mais simplement en extraire les données et les placer dans la table existante. J'utilise Ruby 1.9.2 et Rails 3.

C'est ma table :

create_table "mouldings", :force => true do |t|
  t.string   "suppliers_code"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.string   "name"
  t.integer  "supplier_id"
  t.decimal  "length",         :precision => 3, :scale => 2
  t.decimal  "cost",           :precision => 4, :scale => 2
  t.integer  "width"
  t.integer  "depth"
end

Pouvez-vous me donner un code pour me montrer la meilleure façon de le faire, merci.

408voto

yfeldblum Points 42613
require 'csv'    

csv_text = File.read('...')
csv = CSV.parse(csv_text, :headers => true)
csv.each do |row|
  Moulding.create!(row.to_hash)
end

2 votes

Vous pouvez le mettre dans une tâche Rake, ou dans une action de contrôleur, ou n'importe où vous voulez.....

1 votes

Cela a fonctionné parfaitement. Cependant, j'ai une question de niveau débutant - lorsque j'ai essayé de parcourir les méthodes décrites dans la documentation API de Ruby et Rails, je n'ai pas pu les trouver sur place (j'ai regardé sur les sites officiels de Ruby et Rails, les docs API). Par exemple, je n'ai pas pu trouver quel objet retourne CSV.parse(), je n'ai pas trouvé les méthodes to_hash() et with_indifferent_access()... Peut-être que j'ai regardé au mauvais endroit ou que j'ai manqué un principe de base sur la façon de parcourir les docs des API Ruby & Rails. Quelqu'un peut-il partager la meilleure pratique pour lire les documents relatifs aux API Ruby ?

0 votes

Y a-t-il un moyen de faire cela sans charger tout le fichier en mémoire ?

222voto

Tom De Leu Points 3164

Version plus simple de la réponse de yfeldblum, qui est plus simple et fonctionne bien aussi avec les gros fichiers :

require 'csv'    

CSV.foreach(filename, headers: true) do |row|
  Moulding.create!(row.to_hash)
end

Pas besoin de with_indifferent_access o symbolize_keys et il n'est pas nécessaire de lire d'abord le fichier dans une chaîne de caractères.

Il ne garde pas tout le fichier en mémoire en même temps, mais le lit ligne par ligne et crée un moulage par ligne.

1 votes

C'est mieux pour gérer les fichiers de grande taille, non ? Est-ce qu'il lit une ligne à la fois ?

1 votes

@Simon : en effet. Il ne garde pas tout le fichier en mémoire en même temps, mais le lit ligne par ligne et crée un moulage par ligne.

0 votes

J'ai cette erreur, savez-vous pourquoi? : ActiveModel::UnknownAttributeError : attribut inconnu 'siren;nom_ent;adresse;complement_adresse;cp_ville;pays;region;departement;activite;date;nb_salaries;nom;prenom;civilite;adr_mail;libele_acti;categorie;tel' pour Transaction

14voto

Tilo Points 13833

Le site smarter_csv gem a été spécialement créé pour ce cas d'utilisation : lire les données d'un fichier CSV et créer rapidement des entrées de base de données.

  require 'smarter_csv'
  options = {}
  SmarterCSV.process('input_file.csv', options) do |chunk|
    chunk.each do |data_hash|
      Moulding.create!( data_hash )
    end
  end

Vous pouvez utiliser l'option chunk_size pour lire N lignes csv à la fois, puis utiliser Resque dans la boucle interne pour générer des tâches qui créeront les nouveaux enregistrements, plutôt que de les créer immédiatement - de cette façon, vous pouvez répartir la charge de la génération des entrées sur plusieurs travailleurs.

Voir aussi : https://github.com/tilo/smarter_csv

4 votes

Comme la classe CSV est incluse, je pense qu'il est préférable de l'utiliser plutôt que d'ajouter ou d'installer une gemme supplémentaire. Certes, vous n'avez pas proposé d'ajouter une nouvelle gemme à l'application. Il est si facile d'ajouter une série de gemmes individuelles, chacune pour un but spécifique et avant que vous le sachiez, votre application a des dépendances excessives. (Je me retrouve à éviter consciemment l'ajout de toute gemme. Dans mon atelier, nous devons justifier l'ajout auprès de nos coéquipiers).

2 votes

@Tass, il est également assez facile d'ajouter une série de méthodes individuelles, chacune dans un but spécifique, et avant que vous ne le sachiez, votre application a une logique excessive que vous devez maintenir. Si une gemme fonctionne, qu'elle est bien maintenue et qu'elle utilise peu de ressources ou qu'elle peut être mise en quarantaine dans les environnements pertinents (par exemple, Staging pour les tâches de production) il me semble que toujours une meilleure option pour utiliser la gemme. Ruby et Rails ont pour but d'écrire moins de code.

0 votes

J'ai l'erreur suivante, savez-vous pourquoi ? ActiveModel::UnknownAttributeError : attribut inconnu 'siren;nom_ent;adresse;complement_adresse;cp_ville;pays;region;departement;activite;date;nb_salaries;nom;prenom;civilite;adr_mail;libele_acti;categorie;tel' pour Transaction

5voto

Seamus Abshere Points 4211

Vous pouvez essayer Upsert :

require 'upsert' # add this to your Gemfile
require 'csv'    

u = Upsert.new Moulding.connection, Moulding.table_name
CSV.foreach(file, headers: true) do |row|
  selector = { name: row['name'] } # this treats "name" as the primary key and prevents the creation of duplicates by name
  setter = row.to_hash
  u.row selector, setter
end

Si c'est ce que vous voulez, vous pouvez également envisager de vous débarrasser de la clé primaire à incrémentation automatique de la table et de définir la clé primaire comme suit name . Sinon, s'il existe une combinaison d'attributs qui forment une clé primaire, utilisez-la comme sélecteur. Aucun index n'est nécessaire, cela ne fera qu'accélérer le processus.

4voto

Kalyan M Points 1722

Cela peut aider. Il contient également des exemples de code :

http://csv-mapper.rubyforge.org/

Ou pour une tâche de râteau pour avoir fait la même chose :

http://erikonrails.snowedin.net/?p=212

0 votes

erikonrails.snowedin.net/?p=212 est cassé, s'il vous plaît, j'ai ouvert un problème pour le faire avec la tâche rake ici stackoverflow.com/questions/42515043/

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