Top > Ruby on Railsあれこれ > 複数従業員情報の一括更新

内容

概要

複数従業員情報の一括更新機能を追加します。画面遷移は以下のようなものです。

db_tuto_bupd_1.jpg

一覧画面(index.html.erb)
db_tuto_bupd_2.jpg

編集画面(edit.html.erb)
db_tuto_bupd_3.jpg

データを変更して、【更新内容確認】ボタンをクリックしましょう。
db_tuto_bupd_4.jpg

確認画面(confirm.html.erb)
db_tuto_bupd_5.jpg
↓(編集内容を確認して、【更新】ボタンクリック)
一覧画面(index.html.erb)
db_tuto_bupd_6.jpg

また、編集画面での入力値チェックは、一覧左のチェックボックスがチェックされているものについてのみ行います。入力値エラーがある場合は以下のようになります。

db_tuto_bupd_7.jpg

サンプルのポイント

  • フォームの配列化
  • トランザクション制御

サンプルの使い方

サンプルはあらかじめ用意していないので、以降の項目を参照にして、自分で作成しなければいけません。作成後に、ブラウザからhttp://127.0.0.1:3000/bulk_updateにアクセスしましょう(ポート番号"3000"は適宜、環境に応じて読み替えて下さい)。

ファイル構成

本項で作成・変更するファイル

ファイル名種類
index.html.erbビュー作成
edit.html.erbビュー作成
confirm.html.erbビュー作成
department.rbモデル作成
employee.rbモデル作成
application_helper.rbヘルパー変更
bulk_update_controller.rbコントローラー変更

一覧画面(ビュー)

"ror-db-tutorial\app\views\bulk_update\index.html.erb"を以下の内容で作成します。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
<html>
<head>
  <title>Tutorial: Employee Bulk Update</title>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <%= stylesheet_link_tag 'ror' %>
</head>
<body>
<h2>Tutorial: Employee List For Bulk-Update</h2>
<table border="1">
  <tr style="background-color:pink">
    <th>Id</th><th>Name</th><th>JobType</th><th>Salary</th><th>Department</th>
  </tr>
  <% for e in @employees %>
  <tr style="background-color:<%= cycle('white', 'yellow') %>">
    <td align="right"><%=h e.id %></td>
    <td><%=h e.name %></td>
    <td><%=h e.job_type %></td>
    <td align="right"><%= number_with_delimiter(e.salary, delimiter=",") %></td>
    <td><%=h e.department.name %></td>
  </tr>
  <% end %>
</table>
  
<% form_tag :action => 'edit' do %>
  <%= submit_tag "一括更新処理へ" %>
<% end %>
  
</body>
</html>

編集画面(ビュー)

編集画面を作成しましょう。 "ror-db-tutorial\app\views\bulk_update\edit.html.erb"ファイルを以下の内容で作成します。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
<html>
<head>
  <title>Tutorial: Employee Bulk Update</title>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <%= stylesheet_link_tag 'ror' %>
</head>
<body>
   
<h2>一括更新入力</h2>
  
  <%= list_error_messages_for 'employees' %>
    
  <% form_tag :action => 'confirm' do %>
    <table border="1">
        <tr>
            <th>&nbsp;</th>
            <th>Name</th>
            <th>JobType</th>
            <th>Salary</th>
            <th>Department</th>
        </tr>
        <% @departments = Department.find(:all) %>
        <% for @employee in @employees %>
            <tr>
              <td>
                <%= hidden_field("employee[]", "id") %>
                <%= check_box("employee[]", "update_check") %>
              </td>
              <td><%= text_field("employee[]", "name") %></td>
              <td><%= select("employee[]", "job_type", ['CLERK', 'SALESMAN', 'MANAGER', 'ANALYST', 'PRESIDENT']) %></td>
              <td><%= text_field("employee[]", "salary") %></td>
              <td><%= select("employee[]", "department_id", @departments.collect {|p| [ p.name, p.id ] }) %></td>
            </tr>
        <% end %>
    </table>
    <br />
    <%= submit_tag "更新内容確認" %>
    <%= submit_tag "戻る", {:type => "button", :onClick => "this.form.action='/bulk_update/index'; this.form.submit();"}%>
  <% end %>
  </body>
</html>

↑がHTMLに変換されると↓になります(抜粋)。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
            <tr>
              <td>
                <input id="employee_1_id" name="employee[1][id]" type="hidden" value="1" />
                <input id="employee_1_update_check" name="employee[1][update_check]" type="checkbox" value="1" /><input name="employee[1][update_check]" type="hidden" value="0" />
              </td>
              <td><input id="employee_1_name" name="employee[1][name]" size="30" type="text" value="ALLEN" /></td>
              <td><select id="employee_1_job_type" name="employee[1][job_type]"><option value="CLERK">CLERK</option>
<option value="SALESMAN" selected="selected">SALESMAN</option>
<option value="MANAGER">MANAGER</option>
<option value="ANALYST">ANALYST</option>
<option value="PRESIDENT">PRESIDENT</option></select></td>
              <td><input id="employee_1_salary" name="employee[1][salary]" size="30" type="text" value="1600" /></td>
              <td><select id="employee_1_department_id" name="employee[1][department_id]"><option value="1">ACCOUNTING</option>
<option value="2">RESEARCH</option>
<option value="3" selected="selected">SALES</option>
<option value="4">OPERATIONS</option></select></td>
            </tr>
        
            <tr>
              <td>
                <input id="employee_2_id" name="employee[2][id]" type="hidden" value="2" />
                <input id="employee_2_update_check" name="employee[2][update_check]" type="checkbox" value="1" /><input name="employee[2][update_check]" type="hidden" value="0" />
              </td>
              <td><input id="employee_2_name" name="employee[2][name]" size="30" type="text" value="WARD" /></td>
              <td><select id="employee_2_job_type" name="employee[2][job_type]"><option value="CLERK">CLERK</option>
<option value="SALESMAN" selected="selected">SALESMAN</option>
<option value="MANAGER">MANAGER</option>
<option value="ANALYST">ANALYST</option>
<option value="PRESIDENT">PRESIDENT</option></select></td>
              <td><input id="employee_2_salary" name="employee[2][salary]" size="30" type="text" value="1250" /></td>
              <td><select id="employee_2_department_id" name="employee[2][department_id]"><option value="1">ACCOUNTING</option>
<option value="2">RESEARCH</option>
<option value="3" selected="selected">SALES</option>
<option value="4">OPERATIONS</option></select></td>
            </tr>

color:red>!POINT!</color>
db_tuto_bupd_8.jpg
[block] 「モデルのリストとFORMをマッピングする方法」
.屮蹈奪変数をインスタンス変数にします。
→フォームヘルパーメソッド内でアクセスするため。
"ブロック変数と同じ名前"+"[]"を渡します。
→本HTMLエレメントが、@employeeとバインディングすることを示し
、 →また、"[]"を付けることでこのパラメータが配列であることを示します。
プロパティ名を指定します。
→@employee.idの値を設定することを示します。 [/block]

確認画面(ビュー)

確認画面を作成しましょう。 "ror-db-tutorial\app\views\bulk_update\confirm.html.erb"ファイルを以下の内容で作成します。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
<html>
<head>
  <title>Tutorial: Employee Bulk Update</title>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <%= stylesheet_link_tag 'ror' %>
</head>
<body>
   
<h2>一括更新入力内容確認</h2>
 
<table border="1">
  <tr style="background-color:pink">
    <th>Id</th><th>Name</th><th>JobType</th><th>Salary</th><th>Department</th>
  </tr>
  <% for e in @employees %>
  <% next if e.update_check != "1" %>
  <tr style="background-color:<%= cycle('white', 'yellow') %>">
    <td align="right"><%=h e.id %></td>
    <td><%=h e.name %></td>
    <td><%=h e.job_type %></td>
    <td align="right"><%= number_with_delimiter(e.salary, delimiter=",") %></td>
    <td><%=h r_label(e.department_id, @departments, "id", "name") %></td>
  </tr>
  <% end %>
</table>
  
<% form_tag :action => 'store' do %>
  <%= submit_tag "更新" %>
  <%= submit_tag "戻る", {:type => "button", :onClick => "this.form.action='/bulk_update/edit'; this.form.submit();"}%>
<% end %>
  
</body>
</html>

Employeeモデル

"ror-db-tutorial\app\models\employee.rb"に以下の属性を追加します。

  0
attr_accessor :update_check

で、こうなります。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
class Employee < ActiveRecord::Base
 
  belongs_to :department, :foreign_key => 'department_id'
  belongs_to :address, :foreign_key => 'address_id'
  
  validates_presence_of :name, :salary, :message => "を入力して下さい"
  
  attr_accessor :update_check
  
  #バリデーションエラーメッセージ内のプロパティ名を日本語にマッピングします。
  class << self
    HUMANIZED_ATTRIBUTE_KEY_NAMES = {
      "name" => "氏名",
      "salary" => "給与額"
    }
  
    def human_attribute_name(attribute_key_name)
      HUMANIZED_ATTRIBUTE_KEY_NAMES[attribute_key_name] || super
    end
  end
  
end

Departmentモデル

"ror-db-tutorial\app\models\department.rb"ファイルを以下の内容で作成します。

  0
  1
class Department < ActiveRecord::Base
end

ヘルパーメソッド

"ror-db-tutorial\app\helpers\application_helper.rb"ファイルに以下のように"r_label"メソッドを追加します。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
  #「error_messages_forをリスト対応に」
  #http://hackmylife.net/2007/03/error-messages-for-1.html
  #より
  def list_error_messages_for (object_name, options = {})
    objects = instance_variable_get("@#{object_name}")
    errors = Array.new
    objects.each_with_index { |record, index|
      unless record.errors.empty?
        record.errors.each_full { |msg| errors.push("#{index+1}行目:" + msg)}
      end
    }
    #errors = errors.uniq
    unless errors.empty?
      options = options.symbolize_keys
      content_tag("div",
        content_tag(
          options[:header_tag] || "h4",
          "#{errors.size}個のエラーが発生しました"
        ) +
        content_tag("ul", errors.collect { |msg| content_tag("li", msg) }),
                    "id" => options[:id] || "errorExplanation", 
                    "class" => options[:class] || "errorExplanation"
      )
    end
  end

コントローラー

"ror-db-tutorial\app\controllers\bulk_update_controller.rb"を以下の内容で作成します。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
class BulkUpdateController < ApplicationController
  
  #一覧画面表示
  def index
    @employees = Employee.find(:all, :include=>:department)
    session[:employees] = @employees;
    render :action => 'index.html.erb'
  end
  
  #編集画面表示
  def edit
    @employees = session[:employees]
    render :action => 'edit.html.erb'
  end
  
  #確認画面表示
  def confirm
    if invalid?(sort(params[:employee]))
      render :action => 'edit.html.erb'
      return
    end  
    
    session[:employees] = @employees;
    @departments = Department.find(:all)
    render :action => 'confirm.html.erb'
  end
  
  #データベース更新
  def store
    @employees = session[:employees]
    
    begin
      #トランザクション開始
      ActiveRecord::Base.transaction {
        for e in @employees
          next if e.update_check != "1"
          
          #更新します。補足:"e"はEmployeeのオブジェクト
          Employee.find(e.id).update_attributes(e.attributes)
        end
      }
    rescue => ex
      #例外発生時の処理を記述します。
      render :text => ex
      return
    ensure
      session[:employees] = nil
    end
    index
  end
  
  #複数情報の一括チェックメソッド(private)
  def invalid?(objects)
    @employees = Array.new
    invalid = false;
    objects.each { |o|
      e = Employee.new(o[1])
      e.id = o[1]["id"]
      #更新チェックされているものだけ検証
      if o[1]["update_check"] * "1"
        if !e.valid?
          invalid = true
        end
      end
      @employees.push(e)
    }
    return invalid
  end
  private :invalid?
  
  #Formから送られてくると順番が、おかしくなっているので
  #画面の表示順番通りになるようにソートする
  def sort(objects)
    objects = objects.sort{|a, b|
     a[0].to_i <=> b[0].to_i
    }
    return objects
  end
  private :sort
  
end

ちょっと補足。。。上のコードで、

  0
  1
      e = Employee.new(o[1])
      e.id = o[1]["id"]

っていう箇所があって、この"[1]"という添え字は何?って感じなんですが、"params[:employee]"のデータ構造が↓のようになっているためなんです。

  0
[ ["1", {"id"=>1, "name"=>"ALLEN",・・・}], ["2", {"id"=>2・・・ 

つまり、params[:employee]配列の各要素の[0]には要素番号が、[1]にはハッシュ形式の入力データがそれぞれ格納されています。


添付ファイル: filedb_tuto_bupd_8.jpg 441件 [詳細] filedb_tuto_bupd_6.jpg 436件 [詳細] filedb_tuto_bupd_7.jpg 460件 [詳細] filedb_tuto_bupd_5.jpg 433件 [詳細] filedb_tuto_bupd_4.jpg 416件 [詳細] filedb_tuto_bupd_3.jpg 468件 [詳細] filedb_tuto_bupd_2.jpg 502件 [詳細] filedb_tuto_bupd_1.jpg 533件 [詳細]

リロード   新規 編集 凍結解除 差分 添付 複製 名前変更   ホーム 一覧 単語検索 最終更新 バックアップ リンク元   ヘルプ   最終更新のRSS
Last-modified: 2012-08-08 (水) 23:25:46 (3130d)