Rails上で、CSVファイルを読み込んでみた


やりたいこと

CSVなどのスプレッドシートから一気に読み込みたい。
(新規にシステムを作ったりするときにありがち)

前提

  • Rails 4.2.5(4.x系) SQLiteを使用
  • すでに登録対象のモデルが存在する。

登録対象のモデルに関しては、

objectという名前で
- name:string
- status:integer
- content:text

を持つと仮定する。

実装

ここを参照にした。ほぼこれに近いけど少し違うって感じ

  • ビューに、ファイルを指定してアップロードする処理を呼び出す機能を実装
  • コントローラに、ファイルの受け取りとリダイレクト処理を実装
  • モデルに、内容に基づいた保存処理を実装
  • ルーティングの設定

ビューの実装

アップロード箇所を実装したい表示させたいビューに、こんな感じの実装を行います。

実装箇所
<%= form_tag({controller: "objects", action: "import", method: "post"}, {multipart: true}) do %>
  <%= file_field_tag :file %>
  <%= submit_tag "オブジェクトを一括インポート" %>
<% end %>

コントローラの実装

コントローラは元々ビューやモデルに対する処理を呼び出したりする部分なので、ここではモデルに実装するDBアクセス処理の呼び出しと、リダイレクト処理を実装する。

さっきのビューでは、objectsのコントローラ、にimportという操作をpostメソッドでアクセスする。という意味合いになる。

app/controllers/objects_controller.rb
  def import
    Object.import(params[:file])
    redirect_to "/object"
  end

ここでは、読み込みファイルを引数として、モデルに実装したimportメソッドを呼び出し処理を行った上で、 /object の一覧ページにリダイレクトするという意味合いになる。

モデルの実装

モデルでは、対象のファイルを読み込み1件1件バリデーションして登録する処理を実施している。

関係ある箇所だけ抜粋。

例によってパラメータの精査も忘れず実施。

app/models/object.rb
  def self.import(file)
    CSV.foreach(file.path, headers: true) do |row|

      obj = new
      obj.attributes = row.to_hash.slice(*updatable_attributes)

      obj.save!
    end
  end

  def self.updatable_attributes
    ["name","status","content"]
  end

ルーティングの修正

関係のあるところだけ抜粋

routes.rb
  #objects に限り、post で import 処理を許可する
  resources 'objects', only: :index do
    collection { post :import }
  end