

railsでwebアプリケーションを作成しています。そこでcsvファイルの取り込み機能の実装を現在しています。csv取り込みはできたのですが、csvファイルを取り込む際に、トランザクションを使いたいです。(エラー発生時は全ての変更を取り消し、元の画面に戻ってエラー表示させたいです)
しかし、いまいち実装方法がわからなかったため、質問させていただきました。

[code]

    $ rails -v 
    5.0.0
    
[/code]

controller/costs_controller.rb

[code]

    def import
      if params[:csv_file].blank?
        redirect_to action: 'index', error: '読み込むCSVを選択してください'
      elsif File.extname(params[:csv_file].original_filename) != ".csv"
        redirect_to action: 'index', notice: 'csvファイルのみ読み込み可能です'
      else
        num = Cost.import_by_csv(params[:csv_file])
        redirect_to action: 'index', notice: "#{ num.to_s }件のデータ情報を追加/更新しました"
      end
    end
    
[/code]

model/cost.rb

[code]

    require 'csv'
    class Cost < ActiveRecord::Base
    
      #csvファイルの内容をDBに登録する
      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)
          csv.each do |row|
            next if row.header_row?
    
      #CSVの行情報をHASHに変換
            table = Hash[[row.headers, row.fields].transpose]
    
      #登録済みデータ情報
      #登録されてなければ作成
            cost = caches[table['code']]
    
            if cost.nil?
               cost = new
            end
      #データ情報更新
            cost.attributes = table.slice(*table.except(:created_at, :updated_at).keys)
    
      #バリデーションokの場合は保存
            if cost.valid?
              cost.save!
              imported_num += 1
            end
          end
        end
    
      #更新件数を返す
        imported_num
      end
    end
    
[/code]

views/costs/index.html.slim

[code]

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

route.rb

[code]

    Rails.application.routes.draw do
      resources :costs do
      post :import, on: :collection
    end
    
[/code]

現状のコードは以上になります。トランザクションに関して調べたところ、

[code]

    Cost.transaction do 
      ------ コード -----
    end
    
[/code]

のようにトランザクションを実装するとあるのですが、どこのファイルにどのように記すのかがわかっていません....  
大変お手数なのですが、ご教示頂けますと幸いです。。  
何卒宜しくお願いします。


CC BY-SA 3.0
2016-07-12T16:11:20.033
27579
2016-11-13T13:51:41.520
2016-07-13T01:07:10.157
76
14825
question
ruby-on-rails ruby
railsでcsvファイルを取り込む際に、トランザクションを使いたいです。(エラー発生時は全ての行の変更を取り消し、元の画面に戻ってエラー表示させる)


トランザクションは、一貫性を確保したい一連の処理をまとめるものです。

この場合は `Cost` の更新処理に一貫性を持たせたいということだと思いますので、`Cost.transaction do` ～ `end` の中に
`csv.each` ～ `end` の処理を入れてください。途中エラー (たとえばバリデーションの失敗など) があってロールバックしたい場合には
`raise ActiveRecord::Rollback` とします。

なお、データベース側の制約によって失敗した場合は例外が発生しますので、適切に処理してください。


CC BY-SA 3.0
2016-07-13T01:06:10.247
27583
2016-07-13T01:06:10.247
76
27579
answer