それを作ってみました!
環境は
Ruby 1.9.3
Rails 3.2.5
DB mysql2
さぁ作り始めます!
まずはCSVを作成します。
できればテストでも使いたいので"spec"下に"data"フォルダでも作って置いておきましょう!
モデルから作ります。
cattr_accessor :rejected_rows, instance_reader: false
@@rejected_rows = []#エラーメッセージを表示します。
次にルーティングを指定します。
@@rejected_rows = []#エラーメッセージを表示します。
class << self
def create_from_csv_data(data)
self.rejected_rows.clear#エラーメッセージをクリアにします。
data = NKF::nkf('-S -w', data)
csv = CSV.new(data)
inserted = 0#新規登録件数
updated = 0#更新件数
transaction do
csv.each_with_index do |arr, idx|
next if arr[0] == "登録日時" #CSVの一行目の一つ目が登録日時であってはなりません
next if arr.all? { |value| value.empty? } #valueが空は飛ばします。
user = find_by_number(arr[1]) #userのnumberカラムを探します。
already_number = ( arr[1] != User.find_by_number(arr[1]))#既に登録されているかどうか調べます。
#登録済みのユーザー番号の場合
if user#userのユーザー番号が存在した場合
user.assign_attributes(values_for(arr), without_protection: true)#更新します。
if user.changed?#変更されている場合はsaveします。
if user.save
updated += 1#更新件数に1追加します。
else
self.rejected_rows << [ idx, user ]#エラーの場合は行数とエラーメッセージを挿入します。
end
end
#ユーザー番号が空もしくは登録したいユーザー番号が入っている場合
elsif already_number || arr[1].blank?
user = self.new(values_for(arr), without_protection: true)#新規登録します。
if user.save
inserted += 1#新規登録件数に追加します。
else
self.rejected_rows << [ idx, user ]#エラーメッセージを入れます。
end
else
end
end
raise ActiveRecord::Rollback if rejected_rows.present?#エラーの場合ロールバックしてエラーメッセージを返します。
end
return inserted, updated#無事登録出来た場合は件数を返します。
end
private
def values_for(arr)#ユーザーの内容をここに追加します。(privateにしないと長いので)
{
number: arr[1],#ユーザー番号
family_name: arr[2],#名字
kana_family_name: arr[3],#名字フリガナ
given_name: arr[4],#名前
kana_given_name: arr[5],#名前フリガナ
sex: arr[6],#性別
birth: arr[7],#誕生日
email: arr[8],#メール
zip_code: arr[9],#郵便番号
prefecture: arr[10],#都道府県
address1: arr[11],#住所
address2: arr[12],#住所2
phone: arr[13]#電話番号
}
end
end
def create_from_csv_data(data)
self.rejected_rows.clear#エラーメッセージをクリアにします。
data = NKF::nkf('-S -w', data)
csv = CSV.new(data)
inserted = 0#新規登録件数
updated = 0#更新件数
transaction do
csv.each_with_index do |arr, idx|
next if arr[0] == "登録日時" #CSVの一行目の一つ目が登録日時であってはなりません
next if arr.all? { |value| value.empty? } #valueが空は飛ばします。
user = find_by_number(arr[1]) #userのnumberカラムを探します。
already_number = ( arr[1] != User.find_by_number(arr[1]))#既に登録されているかどうか調べます。
#登録済みのユーザー番号の場合
if user#userのユーザー番号が存在した場合
user.assign_attributes(values_for(arr), without_protection: true)#更新します。
if user.changed?#変更されている場合はsaveします。
if user.save
updated += 1#更新件数に1追加します。
else
self.rejected_rows << [ idx, user ]#エラーの場合は行数とエラーメッセージを挿入します。
end
end
#ユーザー番号が空もしくは登録したいユーザー番号が入っている場合
elsif already_number || arr[1].blank?
user = self.new(values_for(arr), without_protection: true)#新規登録します。
if user.save
inserted += 1#新規登録件数に追加します。
else
self.rejected_rows << [ idx, user ]#エラーメッセージを入れます。
end
else
end
end
raise ActiveRecord::Rollback if rejected_rows.present?#エラーの場合ロールバックしてエラーメッセージを返します。
end
return inserted, updated#無事登録出来た場合は件数を返します。
end
private
def values_for(arr)#ユーザーの内容をここに追加します。(privateにしないと長いので)
{
number: arr[1],#ユーザー番号
family_name: arr[2],#名字
kana_family_name: arr[3],#名字フリガナ
given_name: arr[4],#名前
kana_given_name: arr[5],#名前フリガナ
sex: arr[6],#性別
birth: arr[7],#誕生日
email: arr[8],#メール
zip_code: arr[9],#郵便番号
prefecture: arr[10],#都道府県
address1: arr[11],#住所
address2: arr[12],#住所2
phone: arr[13]#電話番号
}
end
end
get 'users/import', controller: 'users', action: 'import'
post 'users/csv_import', controller: 'users', action: "csv_import"
次にコントローラーです。
post 'users/csv_import', controller: 'users', action: "csv_import"
def import
User.rejected_rows.clear#エラーをクリアします。
end
def csv_import
if params[:file].present?
begin#トランザクション開始です。
inserted, updated = User.create_from_csv_data(params[:file].read)#先ほど作りましたメソッドです。
if User.rejected_rows.empty?#エラーがないなら?
flash[:notice] = I18n.t(:notice, scope: [ :users, :csv_import], inserted: inserted, updated: updated)#i18nを使って更新と新規登録の文章をflashで出しています。
redirect_to action: :import
else
render 'import'
end
rescue => e
flash.now.alert = e.message#エラーメッセージを出しています。
render 'import'
end
else
flash.now.alert = "ファイルを選択して下さい。"
render 'import'
end
end
やっとビューにいけましたUser.rejected_rows.clear#エラーをクリアします。
end
def csv_import
if params[:file].present?
begin#トランザクション開始です。
inserted, updated = User.create_from_csv_data(params[:file].read)#先ほど作りましたメソッドです。
if User.rejected_rows.empty?#エラーがないなら?
flash[:notice] = I18n.t(:notice, scope: [ :users, :csv_import], inserted: inserted, updated: updated)#i18nを使って更新と新規登録の文章をflashで出しています。
redirect_to action: :import
else
render 'import'
end
rescue => e
flash.now.alert = e.message#エラーメッセージを出しています。
render 'import'
end
else
flash.now.alert = "ファイルを選択して下さい。"
render 'import'
end
end
#フォームです。ここは簡単ですね。
<%= form_tag([:users, :csv_import], :multipart => true) do %>
<table>
<th>
<%= label_tag :file, t('.file') %>
</th>
<td>
<%= file_field_tag(:file) %>
</td>
</table>
<div class="submit">
<%= submit_tag t('.upload') %>
</div>
<% end %>
#以下はエラー文
<% if User.rejected_rows.present? %>
<p class="alert">CSVファイルに以下の問題が見つかりました。修正して再びアップロードしてください。</p>
<table class="errors">
<tr>
<th>行番号</th>
<th>エラーの説明</th>
</tr>
<% User.rejected_rows.each do |row| %>
<% idx = row[0]; user = row[1] %>
<tr>
<td><%= idx + 1 %></td>
<td class="error_content">
<ul>
<% user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</td>
</tr>
<% end %>
</table>
<% end %>
なるべくコントローラーを軽くして動きが分かりやすいようにします。<%= form_tag([:users, :csv_import], :multipart => true) do %>
<table>
<th>
<%= label_tag :file, t('.file') %>
</th>
<td>
<%= file_field_tag(:file) %>
</td>
</table>
<div class="submit">
<%= submit_tag t('.upload') %>
</div>
<% end %>
#以下はエラー文
<% if User.rejected_rows.present? %>
<p class="alert">CSVファイルに以下の問題が見つかりました。修正して再びアップロードしてください。</p>
<table class="errors">
<tr>
<th>行番号</th>
<th>エラーの説明</th>
</tr>
<% User.rejected_rows.each do |row| %>
<% idx = row[0]; user = row[1] %>
<tr>
<td><%= idx + 1 %></td>
<td class="error_content">
<ul>
<% user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</td>
</tr>
<% end %>
</table>
<% end %>
モデルで書けるものはモデルに書くことをオススメします。
0 件のコメント:
コメントを投稿