रूबी ऑन रेल्स - एक सीएसवी फ़ाइल से डेटा आयात करें


205

मैं एक सीएसवी फ़ाइल से मौजूदा डेटाबेस तालिका में डेटा आयात करना चाहूंगा। मैं सीएसवी फ़ाइल को सहेजना नहीं चाहता, बस इससे डेटा ले सकता हूं और इसे मौजूदा तालिका में डाल सकता हूं। मैं रूबी 1.9.2 और रेल 3 का उपयोग कर रहा हूं।

यह मेरी तालिका है:

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

क्या आप मुझे ऐसा करने का सबसे अच्छा तरीका दिखाने के लिए कुछ कोड दे सकते हैं, धन्यवाद।

जवाबों:


380
require 'csv'    

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

2
आप इसे एक रेक कार्य में, या एक नियंत्रक क्रिया में, या कहीं भी आपको पसंद कर सकते हैं ....
yfeldblum

1
इसने पूरी तरह से काम किया। हालाँकि, मेरा एक शुरुआती स्तर का प्रश्न है - जब मैंने रूबी और रेल एपीआई प्रलेखन में वर्णित विधियों को ब्राउज़ करने की कोशिश की, तो मैं उन्हें जगह पर खोजने में असमर्थ था (मैंने आधिकारिक रूबी और रेल साइटों, एपीआई डॉक्स पर देखा)। उदाहरण के लिए, मुझे पता नहीं है कि CSV.parse () क्या वस्तु देता है, मुझे to_hash () और with_indifferent_access () के तरीके नहीं मिले ... हो सकता है कि मैंने गलत जगह पर देखा हो या रुब और रेल एपीआई को कैसे पार करना है, इस पर कुछ बुनियादी सिद्धांत को याद किया। डॉक्स। क्या कोई सबसे अच्छा अभ्यास साझा कर सकता है कि रूबी एपीआई डॉक्स कैसे पढ़ें?
व्लादिमीर क्रॉज

2
@daveatflow: हाँ, नीचे मेरा उत्तर देखें, जो एक समय में एक पंक्ति में फ़ाइल में पढ़ता है।
टॉम डे लेउ

1
@ lokeshjain2008, यह ओपी के मॉडल को संदर्भित करता है।
जस्टिन डी।

3
यह विधि अक्षम है! विशाल CSV में राम के उपयोग के आसमान छूते हैं। नीचे वाला बेहतर है।
19

206

Yfeldblum के उत्तर का सरल संस्करण, जो सरल है और बड़ी फ़ाइलों के साथ भी अच्छा काम करता है:

require 'csv'    

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

With_indifferent_access या symbolize_keys के लिए कोई ज़रूरत नहीं है, और पहले एक स्ट्रिंग में फ़ाइल में पढ़ने की कोई आवश्यकता नहीं है।

यह पूरी फ़ाइल को एक बार में मेमोरी में नहीं रखता है, लेकिन लाइन से लाइन में पढ़ता है और प्रति लाइन एक मोल्डिंग बनाता है।


1
यह बड़े फ़ाइल आकारों के प्रबंधन के लिए बेहतर है? क्या यह एक समय में एक पंक्ति में पढ़ता है?
2

1
@Simon: वास्तव में। यह पूरी फ़ाइल को एक बार में मेमोरी में नहीं रखता है, लेकिन लाइन से लाइन में पढ़ता है और प्रति लाइन एक मोल्डिंग बनाता है।
टॉम डी

मेरे पास यह त्रुटि है, क्या आप जानते हैं कि क्यों? ; वर्गीकरण; tel 'के लिए
nico_lrx

1
@AlphaNico अपनी समस्या के साथ एक प्रश्न बनाएँ। यह त्रुटि इस से असंबंधित है, आपके मॉडल ऑब्जेक्ट सिंक से बाहर हैं।
19

इस मामले में, आप इसके लिए टेस्टकेस कैसे लिखते हैं?
अफोलाबी ओलावुवा एकिनवुमी

11

smarter_csvमणि विशेष रूप से इस यूज-केस के लिए बनाया गया था: CSV फ़ाइल से डेटा पढ़ सकते हैं और जल्दी से डेटाबेस प्रविष्टियों बनाने के लिए।

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

आप chunk_sizeएक समय में एन सीएसवी-पंक्तियों को पढ़ने के लिए विकल्प का उपयोग कर सकते हैं , और फिर नौकरियों को उत्पन्न करने के लिए इनर लूप में रेसक्यू का उपयोग कर सकते हैं जो नए रिकॉर्ड बनाएंगे, बजाय उन्हें तुरंत बनाएंगे - इस तरह से आप प्रविष्टियों को उत्पन्न करने का भार फैला सकते हैं कई कार्यकर्ताओं के लिए।

इसे भी देखें: https://github.com/tilo/smarter_csv


3
जैसा कि CSV वर्ग शामिल है, मुझे लगता है कि अतिरिक्त रत्न जोड़ने या स्थापित करने के बजाय इसका उपयोग करना बेहतर है। दी, आपने प्रस्ताव नहीं किया कि आवेदन में एक नया रत्न जोड़ा जाए। व्यक्तिगत रत्नों की एक श्रृंखला को जोड़ना इतना आसान है, प्रत्येक एक विशिष्ट उद्देश्य के लिए और इससे पहले कि आप जानते हैं कि आपके आवेदन में अत्यधिक निर्भरता है। (मैं खुद को सचेत रूप से किसी भी रत्नों के अतिरिक्त से बचने के लिए पाता हूं। मेरी दुकान में हमें अपने साथियों के अतिरिक्त औचित्य की आवश्यकता है।)
टास

1
@ यह भी अलग-अलग तरीकों की एक श्रृंखला को जोड़ने के लिए बहुत आसान है, प्रत्येक एक विशिष्ट उद्देश्य के लिए और इससे पहले कि आप जानते हैं कि आपके आवेदन में अत्यधिक तर्क है जिसे आपको बनाए रखना है। यदि कोई मणि काम करता है, अच्छी तरह से बनाए रखा है, और कम संसाधनों का उपयोग करता है या प्रासंगिक वातावरणों के लिए संगरोध किया जा सकता है (यानी उत्पादन कार्यों के लिए मंचन) यह मुझे हमेशा मणि का उपयोग करने के लिए एक बेहतर विकल्प लगता है । रूबी और रेल सभी कम कोड लिखने के बारे में हैं।
zrisher

मेरी निम्नलिखित त्रुटि है, क्या आप जानते हैं कि क्यों? ActiveModel :: UnknownAttributeError: अज्ञात विशेषता 'भोंपू; nom_ent; Adresse; complement_adresse; cp_ville; भुगतान करता है; क्षेत्र; डिपामेंट; Activité; तिथि; nb_salaries; nom; prenom; civilite; adr_mail; libele_acti; श्रेणी; tel' लेनदेन के लिए
nico_lrx

मैंने इसे एक रेक कार्य पर दिया, कंसोल रिटर्न: रेक निरस्त! NoMethodError: शून्य के लिए अपरिभाषित विधि `करीब’: NilClass stackoverflow.com/questions/42515043/…
मार्कोस आर। ग्वेरा

1
@ CSV प्रोसेसिंग को चुनकर, गति में सुधार और मेमोरी को बचाने के लिए एक नया रत्न जोड़ने का एक अच्छा औचित्य हो सकता है;)
Tilo

5

आप कोशिश कर सकते हैं 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

यदि आप यही चाहते हैं, तो आप तालिका से ऑटो-इन्क्रिमेंट प्राथमिक कुंजी से छुटकारा पाने और प्राथमिक कुंजी सेट करने पर भी विचार कर सकते हैं name। वैकल्पिक रूप से, यदि प्राथमिक कुंजी बनाने वाली विशेषताओं का कुछ संयोजन है, तो चयनकर्ता के रूप में उपयोग करें। कोई सूचकांक आवश्यक नहीं है, यह सिर्फ तेजी से बना देगा।


4

यह मदद कर सकता है। इसके कोड उदाहरण भी हैं:

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

या एक ही काम के लिए एक ही काम के लिए:

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


erikonrails.snowedin.net/?p=212 टूट गया है, कृपया, मैंने रेक कार्य के साथ एक मुद्दा खोला है यहां stackoverflow.com/questions/42515043/…
मार्कोस आर। ग्वेरा

2

एक transactionब्लॉक के अंदर डेटाबेस से संबंधित प्रक्रिया को लपेटना बेहतर है । कोड स्निपेट झटका भाषा मॉडल के लिए भाषाओं के एक सेट को बोने की एक पूरी प्रक्रिया है,

require 'csv'

namespace :lan do
  desc 'Seed initial languages data with language & code'
  task init_data: :environment do
    puts '>>> Initializing Languages Data Table'
    ActiveRecord::Base.transaction do
      csv_path = File.expand_path('languages.csv', File.dirname(__FILE__))
      csv_str = File.read(csv_path)
      csv = CSV.new(csv_str).to_a
      csv.each do |lan_set|
        lan_code = lan_set[0]
        lan_str = lan_set[1]
        Language.create!(language: lan_str, code: lan_code)
        print '.'
      end
    end
    puts ''
    puts '>>> Languages Database Table Initialization Completed'
  end
end

नीचे स्निपेट languages.csvफ़ाइल का एक हिस्सा है,

aa,Afar
ab,Abkhazian
af,Afrikaans
ak,Akan
am,Amharic
ar,Arabic
as,Assamese
ay,Aymara
az,Azerbaijani
ba,Bashkir
...

0

इस मणि का उपयोग करें: https://rubygems.org/gems/active_record_importer

class Moulding < ActiveRecord::Base
  acts_as_importable
end

तो अब आप उपयोग कर सकते हैं:

Moulding.import!(file: File.open(PATH_TO_FILE))

बस यह सुनिश्चित करें कि आपके हेडर आपकी टेबल के कॉलम नामों से मेल खाते हों


0

बेहतर तरीका यह है कि इसे रेक कार्य में शामिल किया जाए। Import.rake फ़ाइल को अंदर / lib / कार्यों / बनाएँ और इस कोड को उस फ़ाइल में डालें।

desc "Imports a CSV file into an ActiveRecord table"
task :csv_model_import, [:filename, :model] => [:environment] do |task,args|
  lines = File.new(args[:filename], "r:ISO-8859-1").readlines
  header = lines.shift.strip
  keys = header.split(',')
  lines.each do |line|
    values = line.strip.split(',')
    attributes = Hash[keys.zip values]
    Module.const_get(args[:model]).create(attributes)
  end
end

इसके बाद इस कमांड को अपने टर्मिनल में रन करें rake csv_model_import[file.csv,Name_of_the_Model]


0

मुझे पता है कि यह पुराना सवाल है, लेकिन यह अभी भी Google में पहले 10 लिंक में है।

एक-एक करके पंक्तियों को सहेजना बहुत कुशल नहीं है क्योंकि यह लूप में डेटाबेस कॉल का कारण बनता है और आप बेहतर तरीके से इससे बचते हैं, खासकर जब आपको डेटा के विशाल हिस्से को सम्मिलित करने की आवश्यकता होती है।

बैच इंसर्ट का उपयोग करना बेहतर (और काफी तेज) है।

INSERT INTO `mouldings` (suppliers_code, name, cost)
VALUES
    ('s1', 'supplier1', 1.111), 
    ('s2', 'supplier2', '2.222')

आप मैन्युअल रूप से ऐसी क्वेरी बना सकते हैं और ऐसा कर सकते हैं Model.connection.execute(RAW SQL STRING)(पुन: संशोधित नहीं) या मणि का उपयोग करें activerecord-import(यह पहली बार 11 अगस्त 2010 को जारी किया गया था) इस मामले में केवल सरणी rowsऔर कॉल में डेटा डालें।Model.import rows

विवरण के लिए मणि डॉक्स का संदर्भ लें


-2

CSV :: तालिका और उपयोग करना बेहतर है String.encode(universal_newline: true)। यह CRLF और CR को LF में परिवर्तित करता है


1
आपका प्रस्तावित समाधान क्या है?
टैस

-3

अगर आप SmartCSV का उपयोग करना चाहते हैं

all_data = SmarterCSV.process(
             params[:file].tempfile, 
             { 
               :col_sep => "\t", 
               :row_sep => "\n" 
             }
           )

यह "\t"नई पंक्तियों द्वारा अलग की गई पंक्तियों के साथ प्रत्येक पंक्ति में टैब सीमांकित डेटा का प्रतिनिधित्व करता है"\n"

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.