创建关联 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
我现在通过 create
和 update
操作调用 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
以下是我的多对多关系中的模型:
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
我现在通过 create
和 update
操作调用 Student.init(student_params)
。
这种方法唯一的缺点是,即使创建学生失败,它也会创建教室。对于我的应用程序,这无关紧要。
我会在 Student
class
def self.init(params)
if i = params[:classroom_ids].find_index(&:empty?)
params[:classroom_ids][i] = create_classroom
end
new(params)
end