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

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

ただいまの
回答率

90.45%

  • Ruby

    9710questions

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

  • Ruby on Rails

    9117questions

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

  • CSV

    889questions

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

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

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,680

glvty83

score 112

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)


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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

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/19 11:37

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

    キャンセル

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

  • ただいまの回答率 90.45%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Ruby

    9710questions

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

  • Ruby on Rails

    9117questions

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

  • CSV

    889questions

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

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