使用一个型号批量插入

时间:2011-06-19 15:39:31

标签: ruby-on-rails

我正在尝试使用textarea和一个提交按钮创建一个表单,允许用户进行批量插入。例如,输入看起来像这样:

0001;MR A
0002;MR B

结果如下:

mysql> select * from members;

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

我对Rails很新,我不确定如何继续这个。我应该使用attr_accessor吗?如何在表单视图中处理失败的验证?有什么例子吗?提前谢谢。

更新

根据MissingHandle的评论,我创建了一个Scaffold并用这个替换了Model的代码:

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

我知道代码远未完成,但我需要知道这是正确的方法吗?

2 个答案:

答案 0 :(得分:1)

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

因此,在此代码中,成员是一个数组,它是各个Member对象的集合。我的想法是,尽可能地,您希望将工作交给Member类,因为它实际上是绑定到数据库表的类,并且您可以期望标准的rails模型行为。为了实现这一点,我重写了所有ActiveRecord模型共有的两种方法:save和valid。 MemberBulk只有在所有成员都有效的情况下才有效,并且只有在保存所有成员的情况下才会计为保存。您可能还应该覆盖errors方法以返回其底层成员的错误,可能指示它在提交的文本中是哪一个。

答案 1 :(得分:-1)

最后我不得不从使用Abstract Class更改为Active Model(不知道为什么,但是当我升级到Rails v3.1时它停止工作)。这是工作代码:

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