RailsでCSV/Excel/OpenOfficeのアップロード機能の実装方法について説明します。
目次
1. Railsプロジェクトの作成
まず、Raislのプロジェクトを作成します。rails new csv_import_test cd csv_import_test
そして、必要なコントローラー、ビュー、モデルを作成します。
rails g controller Products index rails g model Product name:string price:integer released_on:date rake db:migrate
CSV出力するためのデータを作成します。
# db/seeds.rb names = %w(レコーダー イヤホン マイク Webカメラ 洗濯機 冷蔵庫 エアコン ノートPC 40型TV デジタルカメラ) names.each do |name| random = [*1..10].sample # 1から10のランダム値を取得 Product.create! name: name, price: random * 1000, released_on: random.day.ago end
DBにデータを投入します。
rake db:seed
そして、コントローラーのindexアクションを実装します。
# app/contollers/products_contoller.rb class ProductsController < ApplicationController def index @products = Product.all end end
そして、ビューを作成します。
# app/views/products/index.html.erb <h1>商品一覧</h1> <table> <thead> <tr> <th>ID</th> <th>名前</th> <th>値段</th> <th>発売日</th> </tr> </thead> <tbody> <% @products.each do |product| %> <tr> <td><%= product.id %></td> <td><%= product.name %></td> <td><%= product.price %></td> <td><%= product.released_on %></td> </tr> <% end %> </tbody> </table>
そして、最後にルートを追加します。
Rails.application.routes.draw do root 'products#index' resources 'products', only: :index end
rails s
でサーバーを起動して画面を確認しましょう。
2. CSVのアップロード機能の実装/
では、アップロード機能を実装します。CSVを処理できるようにするために、Ruby標準ライブラリのcsv
を追加します。
# config/application.rb require File.expand_path('../boot', __FILE__) require 'rails/all' require 'csv'
ビューにファイルをアップロードする入力フィールドを追加します。
# app/views/products/index.html.erb <!-- ページの一番下に追加 --> <%= form_tag import_products_path, multipart: true do %> <%= file_field_tag :file %> <%= submit_tag "インポート" %> <% end %>
ルートを追加します。
# config/routes.rb resources 'products', only: :index do collection { post :import } end
コントローラーでファイルを受け取り、リダイレクトします。
# app/controllers/products_controller.rb def import # fileはtmpに自動で一時保存される Product.import(params[:file]) redirect_to root_url, notice: "商品を追加しました。" end
CSVを読み込んで、DBに登録するインポート処理を実装します。
# app/models/product.rb class Product < ActiveRecord::Base def self.import(file) CSV.foreach(file.path, headers: true) do |row| # IDが見つかれば、レコードを呼び出し、見つかれなければ、新しく作成 product = find_by(id: row["id"]) || new # CSVからデータを取得し、設定する product.attributes = row.to_hash.slice(*updatable_attributes) # 保存する product.save! end end # 更新を許可するカラムを定義 def self.updatable_attributes ["name", "price", "released_on"] end end
サーバーを再起動して、ファイルをアップロードします。
アップロードするファイルの内容は次の通りです。
id,name,price,released_on 10,一眼デジタルカメラ,300,2014-11-13 11,Bar,100,2014-12-01
では、アップロードしてみます。
アップロードすると、ID10の商品が更新され、ID11の商品が追加されています。
3. Excelのインポート機能の実装/
次は、Excelのインポート機能の実装です。rooというExcel, CSV, OpenOffice, GoogleSpreadSheetを開くことができるGemを利用します。
# Gemfile gem 'roo'
バンドルインストールします。
bundle install
ファイル読み込みの箇所を拡張子に合わせて、Rooを使って読み込むようにします。
# app/models/product.rb class Product < ActiveRecord::Base def self.import(file) spreadsheet = open_spreadsheet(file) header = spreadsheet.row(1) (2..spreadsheet.last_row).each do |i| # {カラム名 => 値, ...} のハッシュを作成する row = Hash[[header, spreadsheet.row(i)].transpose] # IDが見つかれば、レコードを呼び出し、見つかれなければ、新しく作成 product = find_by(id: row["id"]) || new # CSVからデータを取得し、設定する product.attributes = row.to_hash.slice(*updatable_attributes) # 保存する product.save! end end def self.open_spreadsheet(file) case File.extname(file.original_filename) when '.csv' then Roo::Csv.new(file.path, nil, :ignore) when '.xls' then Roo::Excel.new(file.path, nil, :ignore) when '.xlsx' then Roo::Excelx.new(file.path, nil, :ignore) when '.ods' then Roo::OpenOffice.new(file.path, nil, :ignore) else raise "Unknown file type: #{file.original_filename}" end end ... end
では、サーバーを再起動して、ファイルをアップロードしましょう。
今回は、OpenOfficeで次のようなファイルを作成しました。
アップロードすると、ID11の商品が更新され、ID12の商品が追加されていることがわかります。
以上です。