创建关联 has_many :through 具有嵌套形式的记录

Creating associated has_many :through records with nested forms

以下是我的多对多关系中的模型:

class Student < ActiveRecord::Base
  has_many :classroom_memberships, :dependent => :destroy
  has_many :classrooms, :through => :classroom_memberships
end

class ClassroomMembership < ActiveRecord::Base
  belongs_to :classroom
  belongs_to :student
end

class Classroom < ActiveRecord::Base
  has_many :classroom_memberships
  has_many :students, :through => :classroom_memberships
end

创建新学生时,我想让用户能够select多个教室并且创建新教室 即时。

(我已经能够让它与一对多关系一起工作。)

这是我目前在学生表格中的内容:

<%= f.association :classrooms,
  collection: Classroom.all(order: 'name'),
  prompt: "Choose or type a new classroom name",
  multiple: true,
  input_html: { :class => "shown" } %>

这让我可以做这个漂亮的多 select 框(使用 selectize.js):

当我提交表单时,通过的参数是:

=> {
     "first_name" => "Tyene",
      "last_name" => "Sand",
       "birthday" => "January 1, 2013",
         "gender" => "F",
       "about_me" => "",
      "family_id" => "1",
  "classroom_ids" => [
    [0] "",
    [1] "1",
    [2] "2"
  ]
}

当调用 Student.new(student_params) 时,Rails 神奇地知道在 classroom_memberships table 中创建 2 条新记录并且一切顺利。

即时创建新教室

但是,如果我让用户将一个全新的教室添加到列表中,Rails 会抛出此错误:

Couldn't find all Classrooms with IDs (1, 2, 0) (found 2 results, but was looking for 3)

显然,如果不先为 select 列表中的第三项创建新的 classroom,它就无法创建 classroom_memberships 记录。

我的问题:

我如何(或在哪里)插入一些代码来检查参数中的 classroom_ids 列表并在 Rails 检查之前创建 classroom 记录查看 classroom_id 是否存在?

我尝试将代码放入 classroom_membershp.rb 模型中,如下所示:

  def classroom_id=(val)
    # if we've got an integer, they selected an existing record;
    # otherwise, we're creating a brand new one
    if val.numeric?
      write_attribute(:classroom_id, val.to_i)
    else
      create_classroom(:name => val)
    end
  end

但是代码永远不会出现在这里!它试图在此点之前通过 SELECT 找到 classroom 并爆炸。这种方法在一对多关系中对我有用。

感谢@lx00st 的回答,我得到了什么:

/app/models/student.rb中:

  def self.init(params)
    params[:classroom_ids].each_with_index do |c, i|
     # a non-blank, non-numeric classroom ID indicates a new record
     if !c.blank? && !c.numeric?
        params[:classroom_ids][i] = Classroom.create(:name => c).id
      end
    end
    new(params)
  end

我现在通过 createupdate 操作调用 Student.init(student_params)

这种方法唯一的缺点是,即使创建学生失败,它也会创建教室。对于我的应用程序,这无关紧要。

我会在 Student class

中使用一些特殊的 self.init(params) 方法
def self.init(params)
  if i = params[:classroom_ids].find_index(&:empty?)
    params[:classroom_ids][i] = create_classroom 
  end 
  new(params)
end