1

trying to create an "award" record. I have 2 selection boxes calling the Employee model to populate the Nominator and Nominee in the Award.

The Employee table gets dropped and recreated every night via a batch job, so I need to capture all the metadata attributes (first_name, last_name, etc) of the Nominator and Nominee at the time of award creation.

Normally, I would just have an employee_id foreign key in the award model, then call the employee attributes dynamically, but the employee data is dropped each night, so I'm totally stuck on the logic on how to do this. Please help! And thanks in advance!

class AwardsController < ApplicationController
def create
    @award = Award.new(params[:award])

    respond_to do |format|
      if @award.save
        format.html { redirect_to new_award_path, notice: 'Award was successfully created.' }
        format.json { render json: @award, status: :created, location: @award }
      else
        format.html { render action: "new" }
        format.json { render json: @award.errors, status: :unprocessable_entity }
      end
    end
  end
end

-

<%= form_for(@award) do |f| %>
  <% if @award.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@award.errors.count, "error") %> prohibited this award from being saved:</h2>

      <ul>
      <% @award.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %> 
<h4>I am...</h4>
  <label>Name</label>
    <%= f.collection_select :nominator_username, Employee.active.order(:last_name), :username, :lastfirst, :include_blank => true %>


<hr>
<h4>I would like to nominate...</h4>
    <label>Name</label>
    <%= f.collection_select :nominee_username, Employee.active.order(:last_name), :username, :lastfirst, :include_blank => true %>


    <h4>For providing the following...</h4>
    <%= f.text_area :award_description, :size => "50x7" %>

    <button type="submit">Submit</button>
    <div class="spacer"></div><p>

<% end %>

-

class CreateAwards < ActiveRecord::Migration
  def change
    create_table :awards do |t|
        t.string    :nominator_first_name
        t.string    :nominator_last_name
        t.string    :nominator_username
        t.string    :nominator_phone
        t.string    :nominator_employee_number
        t.string    :nominator_mail_station
        t.string    :nominee_first_name
        t.string    :nominee_last_name
        t.string    :nominee_username
        t.string    :nominee_employee_number
        t.string    :nominee_phone
        t.string    :nominee_mail_station
        t.text      :award_description
        t.timestamps
    end
  end
end

UPDATE:

Okay, I created a rake task:

#\lib\tasks\import.rake
require 'csv'

desc "Import employees from csv file"
task :import => [:environment] do

  file = "db/users.csv"

  CSV.foreach(file, :headers => true) do |row|
    Employee.find_or_create_by_username({
      :username => row[0],
      :last_name => row[1],
      :first_name => row[2],
      :employee_number => row[3],
      :phone => row[4],
      :mail_station => row[5]
    }
    )
  end
end

When I run rake import.rake, I get:

*Don't know how to build task 'import.rake'*

/.rvm/gems/ruby-1.9.3-p286/gems/rake-10.0.4/lib/rake/task_manager.rb:49:in `[]'
/.rvm/gems/ruby-1.9.3-p286/gems/rake-10.0.4/lib/rake/application.rb:142:in `invoke_task'
/.rvm/gems/ruby-1.9.3-p286/gems/rake-10.0.4/lib/rake/application.rb:101:in `block (2 levels) in top_level'
/.rvm/gems/ruby-1.9.3-p286/gems/rake-10.0.4/lib/rake/application.rb:101:in `each'
/.rvm/gems/ruby-1.9.3-p286/gems/rake-10.0.4/lib/rake/application.rb:101:in `block in top_level'
/.rvm/gems/ruby-1.9.3-p286/gems/rake-10.0.4/lib/rake/application.rb:110:in `run_with_threads'
/.rvm/gems/ruby-1.9.3-p286/gems/rake-10.0.4/lib/rake/application.rb:95:in `top_level'
/.rvm/gems/ruby-1.9.3-p286/gems/rake-10.0.4/lib/rake/application.rb:73:in `block in run'
/.rvm/gems/ruby-1.9.3-p286/gems/rake-10.0.4/lib/rake/application.rb:160:in `standard_exception_handling'
/.rvm/gems/ruby-1.9.3-p286/gems/rake-10.0.4/lib/rake/application.rb:70:in `run'
/.rvm/gems/ruby-1.9.3-p286/gems/rake-10.0.4/bin/rake:33:in `<top (required)>'
/.rvm/gems/ruby-1.9.3-p286/bin/rake:23:in `load'
/.rvm/gems/ruby-1.9.3-p286/bin/rake:23:in `<main>'

UPDATE - Here is my final rake task. I am getting an error with the final update_column block: *undefined method 'update_column' for nil:NilClass*

require 'csv'

desc "Import employees from csv file"
task :import => [:environment] do

  file = "db/users1.csv"

    to_be_deactivated = {}
        Employee.all.each do |e|
        to_be_deactivated[ e.employee_number ] = true
    end

  CSV.foreach(file, headers: true) do |row|
    e = Employee.find_or_create_by_employee_number( row[:employee_number] )

    to_be_deactivated[ e.employee_number ] = false

    e.update_attributes(
      :last_name => row[1],
      :first_name => row[2],
      :phone => row[4],
      :mail_station => row[5]) 

    e.save
    end

    to_be_deactivated.each do |n|
        e = Employee.find_by_employee_number( n )
        e.update_column(:active, false)
    end

end
4

1 回答 1

1

If this is a Rails application, and you're not just piggy-backing on a legacy database, then I would recommend to not drop and re-import the employee table entirely, but to run a Rails-based import job, which uses the accounting employee-number to match records, and then does a find_or_create_by_employee_number -- so your Rails ids stay the same throughout your app.

You can set-up Rails to access multiple databases, and you can then read from the accounting DB and match those records against the Rails app's DB.

If you can't get direct read access to the accounting DB, then use the nightly dump, to populate a temporary DB, which you access as a secondary DB in Rails, and then match the employee records with a script/runner or rake based script that you can start via cron or delayed job.

That is probably the cleaner way to deal with this.

Then you can just use the Rails id for the employees in your awards model.

Dropping the database table, and re-creating it every night is not a good idea, because all your references become useless.


If you don't like that suggestion, then you could use the internal accounting employee number as the unique identifier in the awards model. You don't need to store all the other attributes, because the employee number will not change for the same person -- just do a find_by_employee_number


 to_be_deleted = {} # empty Hash
 Employee.all.each do |e|
     to_be_deleted[ e.employee_number ] = true
 end

 # now run your rake task with LDAP import.
 CSV.foreach(file, :headers => true) do |row|
   e = Employee.find_or_create_by_employee_number( row[:employee_number] )

   to_be_deleted[ e.employee_number ] = false

   #... 
   e.update_attributes( ... )
   e.save
 end

 # any records which were not updated by the LDAP import
 # need to be deleted:

 to_be_deleted.each do |n|
    e = Employee.find_by_employee_number( n )
    e.destroy  # also deletes associated records; otherwise use delete
 end
于 2013-04-25T15:52:16.320 回答