开发者

Bulk insert using one model

I'm trying to create a form using textarea and a submit button that will allow users to do bulk insert. For example, the input would look like this:

0001;MR A
0002;MR B

The result would look like this:

mysql> select * from members;

+------+------+------+
|  id  |  no  | name |
+------+------+------+
|   1  | 0001 | MR A |
+------+------+------+
|   2  | 0002 | MR B |
+------+------+------+

I'm very new to Rails and I'm not sure on how to proceed with this one. Should I use attr_accessor? How do I handle failed validations in the form view? Is there any example? Thanks in advance.

Update

Based on MissingHandle开发者_开发百科's comment, I created a Scaffold and replace the Model's code with this:

class MemberBulk < ActiveRecord::Base

  attr_accessor :member

  def self.columns
    @columsn ||= []
  end

  def self.column(name, sql_type = nil, default = nil, null = true)
    columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
  end

  column :data, :text

  validates :data, :create_members, :presence => true

  def create_members
    rows = self.data.split("\r\n")

    @member = Array.new

    rows.each_with_index { |row, i|
      rows[i] = row.strip
      cols = row.split(";")

      p = Member.new
      p.no = cols[0]
      p.name = cols[1]

      if p.valid?
        member << p
      else
        p.errors.map { |k, v| errors.add(:data, "\"#{row}\" #{v}") }
      end
    }
  end

  def create_or_update
    member.each { |p|
      p.save
    }
  end
end

I know the code is far from complete, but I need to know is this the correct way to do it?


class MemberBulk < ActiveRecord::Base

  #Tells Rails this is not actually tied to a database table
  # or is it self.abstract_class = true
  # or @abstract_class = true
  # ?
  abstract_class = true

  # members holds array of members to be saved
  # submitted_text is the data submitted in the form for a bulk update
  attr_accessor :members, :submitted_text
  attr_accessible :submitted_text

  before_validation :build_members_from_text

  def build_members_from_text
    self.members = []
    submitted_text.each_line("\r\n") do |member_as_text|
      member_as_array = member_as_text.split(";")
      self.members << Member.new(:number => member_as_array[0], :name => member_as_array[1])
    end
  end

  def valid?
    self.members.all?{ |m| m.valid? }
  end

  def save
    self.members.all?{ |m| m.save }
  end

end

class Member < ActiveRecord::Base

  validates :number,  :presence => true, :numericality => true
  validates :name,    :presence => true

end

So, in this code, members is an array that is a collection of the individual Member objects. And my thinking is that as much as possible, you want to hand off work to the Member class, as it is the class that will actually be tied to a database table, and on which you can expect standard rails model behavior. In order to accomplish this, I override two methods common to all ActiveRecord models: save and valid. A MemberBulk will only be valid if all it's members are valid and it will only count as saved if all of it's members are saved. You should probably also override the errors method to return the errors of it's underlying members, possibly with an indication of which one it is in the submitted text.


In the end I had to change from using Abstract Class to Active Model (not sure why, but it stoppped working the moment I upgrade to Rails v3.1). Here's the working code:

class MemberBulk
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming

  attr_accessor :input, :data

  validates :input, presence: true

  def initialize(attributes = {})no
    attributes.each do |name, value|
      send("#{name}=", value) if respond_to?("#{name}=")
    end
  end

  def persisted?
    false
  end  

  def save
    unless self.valid?
      return false
    end

    data = Array.new

    # Check for spaces
    input.strip.split("\r\n").each do |i|
      if i.strip.empty?
        errors.add(:input, "There shouldn't be any empty lines")
      end

      no, nama = i.strip.split(";")

      if no.nil? or nama.nil?
        errors.add(:input, "#{i} doesn't have no or name")
      else
        no.strip!
        nama.strip!

        if no.empty? or nama.empty?
          errors.add(:input, "#{i} doesn't have no or name")
        end
      end  

      p = Member.new(no: no, nama: nama)
      if p.valid?
        data << p
      else
        p.errors.full_messages.each do |error|
          errors.add(:input, "\"#{i}\": #{error}")  
        end        
      end             
    end # input.strip    

    if errors.empty?
      if data.any?

        begin
          data.each do |d|
            d.save
          end
        rescue Exception => e
          raise ActiveRecord::Rollback
        end

      else
        errors.add(:input, "No data to be processed")
        return false
      end
    else
      return false
    end

  end # def
end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜