0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails】CSVの中身を整形してDBにデータを保存しよう

Last updated at Posted at 2021-06-11

業務でCSVの整形処理を扱いました。
今後も割と扱うこと多いらしいので扱い方をざっくりまとめておきます。
実装手順に沿ってまとめたので以下に従って実装すればCSVを整形してDBにデータを保存できるようになるはずです。

前提

  • ruby 2.3.1
  • Rails4.2.3
  • hamlからCSVを送信する

①CSVをビューから受け取る

Railsのビューファイルからフォームを送るとActionDispatch::UploadedFileオブジェクトとして送信されます。
CSVを整形するためにはこれをFileオブジェクトに変換する必要があります。

file = params[:file].tempfile

ActionDispatch::UploadedFileオブジェクトには tempfile というキーでFileオブジェクトが格納されており
params[:file].tempfile とすることでFileオブジェクトに変換できます。

②CSV.forEachでCSVを1行ずつに分解

rubyのCSVクラスの持つ便利なメソッドに CSV.foreEach というのがあります。
これを使うとCSVのデータを1行ずつに分解できます。

file = params[:file].tempfile

# ここから追記
CSV.forEach(file, header_row: true) do |row|
  row
end

こんな感じで CSV.forEach の中で各行に対して処理を行います。このとき、第一引数はFilleオブジェクトである必要があります。
①でFileオブジェクトに変換したのはこのためです。

ここでポイントになるのが header_row: true というオプションで、これを使うとheader(列名)の値とrow(列に対応する値)
の部分をキーバリューの組として取り出せます。例えば

日付 気温
2021/06/11 30℃

こんな感じのCSVがあれば以下のようなオブジェクトがブロック変数rowに代入されます。

<CSV::ROW '日付':'2021/06/11', '気温':'30℃' >

このオブジェクトの便利なところとして、キーを指定して値を取り出せます。

row = <CSV::ROW '日付':'2021/06/11', '気温':'30℃' >
row['日付'] 
=> '2021/06/11'
row['日付']  = '2021/06/10'
row['日付'] 
=> '2021/06/10'

次のステップでこれを利用します。

③各行のデータをハッシュに変換

各行のデータを取り出す各行のデータをハッシュに変換する。このとき上記で確認したCSV::ROWオブジェクトが活躍します。

キーを指定して値を取り出せるので全体的なイメージとして以下のようなメソッドを組みます。

def convert_csv_to_hash(row)
  params = {
    date: row['日付']
    temperature: row['気音']
  }
  return params
end

# ここまで追記

file = params[:file].tempfile
CSV.forEach(file, header_row: true) do |row|
  params = convert_csv_to_hash(row)
end

こうすることでCSV.forEachの各行をrailsで扱えるハッシュに変換できます。

④変換したハッシュを元にDBにデータを保存

ハッシュに変換できたらDBにデータを保存します。

def convert_csv_to_hash(row)
  params = {
    date: row['日付']
    temperature: row['気音']
  }
  return params
end

file = params[:file].tempfile
CSV.forEach(file, header_row: true) do |row|
  params = convert_csv_to_hash(row)

# 追加。Hogeは保存したいモデル名
 Hoge.create(params)
end

⑤この処理を何かしらのクラスで行う

ここまでで処理自体は記述できたのでこれをコントローラーなりに記述しましょう。
僕が今回実装したものだとCSVを扱う専用のService層を作ってそこに記述しました。

⑥プラスアルファ…例外処理について

CSVからデータを保存する際は複数データを纏めて保存する仕様のため、保存に失敗した際に具体的に
どのデータの保存に失敗したのかを検知する仕組みがあったほうがいいと思います。
例えば今回僕は以下のような例外処理を組みました。

class ImportCSV
    def convert_csv_to_hash(row)
      params = {
        date: row['日付']
        temperature: row['気音']
      }
      return params
    end

    file = params[:file].tempfile
    CSV.forEach(file, header_row: true).with_index(1) do |row, line_num|
    params = convert_csv_to_hash(row)

    # ここから追記
      begin
        Hoge.create(params)
      rescue e
        return "#{line_num}行目が不正です。エラー内容:#{e.message}"
      end
    end
end

あとはエラーメッセージをコントローラーで受け取ってビューに表示する、Slackやメールで通知するなどすればOK。

感想

CSVの組み込みメソッドだけでほとんど処理できたので思ったよりかんたんでした。
まだまだ使えるメソッドはたくさんあると思うので今後もっと深堀りしていきたいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?