创建关联的has_many:通过嵌套表单的记录

时间:2015-03-13 14:29:15

标签: ruby-on-rails nested-forms has-many-through

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

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

在创建新学生时,我希望让用户能够选择多个教室以及即时创建新教室

(我已经能够通过一对多关系来实现这一点。)

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

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

这允许我做这个漂亮的多选框(使用selectize.js):

enter image description here

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

=> {
     "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表中创建2条新记录,一切都很好。

即时创建新教室

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

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

显然,如果没有为选择列表中的第三项创建新的classroom_memberships,它就无法创建classroom记录。

我的问题:

如何(或在哪里)可以插入一些代码来检查参数中的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

但代码永远不会到达!它尝试在此之前通过classroom找到SELECT并炸毁。这种方法在一对多关系中对我有用。

由于@ 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

我现在正在Student.init(student_params)create行动中致电update

这种方法的唯一缺点是即使学生创作失败也会创建课堂。对于我的申请,这没关系。

1 个答案:

答案 0 :(得分:1)

我会在Student

中使用一些特殊的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