質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

1回答

3826閲覧

railsのcsvファイルの読み込みについての質問です。(読み込む際にデータを新規登録するか、更新するか判別させたいです)

glvty83

総合スコア135

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

1クリップ

投稿2016/07/14 07:31

編集2016/07/15 07:40

railsのcsvファイルの読み込みについての質問です。(読み込む際にファイルデータをDBに新規登録するか、更新するか判別させたいです)
codeカラムをキーとしてcodeがDBとcsvファイルで照らし合わせ、数字が一致すればupdate, しなければcreateを実行して欲しいのですが、現状、updateが実行されず、createしかされません。
お手数おかけしますが、updateを行うには何が足りないのかご教示いただけましたら嬉しいです....
以下が現在のコードです。

model/cost.rb

require 'csv' class Cost < ActiveRecord::Base validates :code, presence: true, uniqueness: true validates :name, presence: true, uniqueness: true validates :cost_class, presence: true validates :budget_class, presence: true def self.import_by_csv(file) imported_num = 0 #更新データの件数の初期値 open(file.path, 'r:cp932:utf-8', undef: :replace) do |f| csv = CSV.new(f, :headers => :first_row) caches = Cost.all.index_by(&:code) Cost.transaction do #トランザクションの開始 csv.each do |row| next if row.header_row? table = Hash[[row.headers, row.fields].transpose] cost = caches[table['code']] || new #この辺りが怪しいと感じています cost.attributes = table.slice(*table.except(:id, :created_at, :updated_at).keys) # ここのコードもよくわかっていません... cost.save! #失敗したら例外発生 imported_num += 1 end end end imported_num end end

controllers/costs_controller.rb

class CostsController < ApplicationController def import if params[:csv_file].blank? redirect_to costs_path, alert: '読み込むCSVを選択してください' elsif File.extname(params[:csv_file].original_filename) != ".csv" redirect_to master_costs_path, alert: 'csvファイルのみ読み込み可能です' else imported_row_count = Cost.import_by_csv(params[:csv_file]) redirect_to costs_path, alert: "#{ imported_row_count }件のデータ情報を追加/更新しました" end rescue ActiveRecord::RecordInvalid => e redirect_to costs_path, alert: "不正なデータが含まれています:#{ e.record.errors.full_messages }" rescue ActiveModel::UnknownAttributeError redirect_to costs_path, alert: '適切なファイルを選択してください' end end

index.html.slim

= form_tag import_costs_path, method: :post, multipart: true do = file_field_tag :csv_file = submit_tag 'CSV読み込み'

現状このコードでcsvファイルを読み込むとcodeがない場合にはデータがDBに新規作成されますが、codeがすでにある場合は
""不正なデータが含まれています:["Codeはすでに存在します", "Nameはすでに存在します"]"" と表示され、更新されません。=> 新規登録を前提とする警告が出てしまっている気がします...

ログ

ActiveRecord::SchemaMigration Load (0.6ms) SELECT "schema_migrations".* FROM "schema_migrations" Processing by CostsController#import as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"*******", "csv_file"=>#<ActionDispatch::Http::UploadedFile:0x007fe88db51800 @tempfile=#<Tempfile:/var/folders/dm/******/T/*****.csv>, @original_filename="costs.csv", @content_type="text/csv", @headers="Content-Disposition: form-data; name=\"csv_file\"; filename=\"costs.csv\"\r\nContent-Type: text/csv\r\n">, "commit"=>"CSV読み込み"} Cost Load (1.2ms) SELECT "costs".* FROM "costs" (3.7ms) BEGIN Cost Exists (0.5ms) SELECT 1 AS one FROM "costs" WHERE "costs"."code" = $1 LIMIT $2 [["code", 1], ["LIMIT", 1]] Cost Exists (1.2ms) SELECT 1 AS one FROM "costs" WHERE "costs"."name" = $1 LIMIT $2 [["name", "あ,\"\\え"], ["LIMIT", 1]] (0.3ms) ROLLBACK Redirected to http://localhost:3000/costs Completed 302 Found in 413ms (ActiveRecord: 13.3ms) Started GET "/costs" for ::1 at 2016-07-14 16:16:19 +0900 Processing by CostsController#index as HTML Rendering costs/index.html.slim within layouts/application Cost Load (1.3ms) SELECT "costs".* FROM "costs" ORDER BY "costs"."code" ASC LIMIT $1 OFFSET $2 [["LIMIT", 10], ["OFFSET", 0]] (0.5ms) SELECT COUNT(*) FROM "costs" Rendered costs/index.html.slim within layouts/application (177.9ms) Completed 200 OK in 1564ms (Views: 1477.7ms | ActiveRecord: 1.8ms)

コントローラーで何かが足りない気はしますが、わかりません。
長くなってしまいすみませんが、回答いただけましたら大変幸いです。
よろしくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

validates :code, presence: true, uniqueness: true
validates :name, presence: true, uniqueness: true

としているので、
このエラーは、

すでに code や name が同じものを追加しようとしていること

を示しています。

現状のコードでは、
すでにあるrecord の内容を更新しようとしているにもかかわらず、
常に新規レコード追加になってしまっているようです。

record を探しているのは
cost = caches[table['code']]
ですね。
caches[] の値, table[] の値、存在する record をみつけることができているかを確認するとよいとおもいます。
(現状では常に record が見つからずに record の新規登録になってしまっていると予想されます)

table['code'], caches[table['code']] を p してみたり、 debugger で値を確認するとよいです。

質問とは関係しませんが、1つコメントがあります。
caches = Cost.all.index_by(&:code)
は メモリ上に Cost テーブルの内容が全部展開されてしまっています。
record の (id、code) の組だけをメモリーに展開し、 code から id を探すようにすると良いと思います。
あるいは、 DB 上では code で index をはり、 Cost に対して find_or_create_by() で code で毎回検索をしても良いかもしれません。
これでは速度がでないことが確認できてから、自力でキャッシュなの処理を考えれば良いです。

参考:

... 検索条件を指定して初めの1件を取得し、1件もなければ作成する ...

投稿2016/07/15 22:13

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

glvty83

2016/07/19 02:37

ご回答ありがとうございました。また、返信が遅れてしまい申し訳ありませんでした。 指摘いただいた cost = caches[table['code']] の理解が自分の中で不十分なのでこのコードの意味の再確認から始めていきたいと思います。おかげさまで頭が整理されましたのでこのヒントを参考にしながらもう少し考えてみたいと思います。解決できたらこちらにコードを貼らせていただきますので、すみませんがしばしお待ち下さい....
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問