我深入搜索网页,以便找到一种干净简单的方法来处理has_many :through relation
的连接模型上的属性初始化,但我没有找到满足我需要的最佳解决方案。
在我下面提供的问题中,我需要在创建或更新role
对象时自动设置Training
联接模型的属性Course
。
这是我的模特:
QUALIFICATIONS = ["Theoretical Instructor", "Practical Instructor"]
class Course < ActiveRecord::Base
has_many :trainings, dependent: :destroy
has_many :theoretical_instructors, through: :trainings, source: :trainer, conditions: { "trainings.role" => "Theoretical Instructor" }
accepts_nested_attributes_for :theoretical_instructors
has_many :practical_instructors, through: :trainings, source: :trainer, conditions: { "trainings.role" => "Practical Instructor" }
accepts_nested_attributes_for :practical_instructors
end
class Trainer < ActiveRecord::Base
has_many :trainings, dependent: :destroy
has_many :courses, through: :trainings
end
class Training < ActiveRecord::Base
belongs_to :trainer
belongs_to :course
# Join model has the :role attribute, that I wish I could validate this way:
# validates :role, presence: true, inclusion: { in: QUALIFICATIONS }
end
这个模型背后的基本原理是我想在一个表中保存Training
个对象。我不想创建TheoreticalInstructor
和PracticalInstructor
联接模型(可能爆炸表的数量)来解决这个问题。
此视图提供了提交新Course
:
<%= form_for @course do |course_form| %>
<%- # fields for course attributes, as usual... %>
<%= course_form.label :theoretical_instructor_ids %><br />
<%= course_form.select :theoretical_instructor_ids, Trainer.all.map { |x| [[x.name, x.surname].join(" "), x.id] }, { }, { multiple: true } %>
<%= course_form.label :practical_instructor_ids %><br />
<%= course_form.select :practical_instructor_ids, Trainer.all.map { |x| [[x.name, x.surname].join(" "), x.id] }, { }, { multiple: true } %>
<%= course_form.submit %>
<% end%>
问题是:为了使@course = Course.new(params[:course])
Course
控制器中的唯一代码行需要在提交上一个表单时保存此关联,我该怎么做?
与this question不同我在创建新Trainer
时不想创建新的Course
对象:我想从已经存在于数据库中的对象中选择它们(通过多选输入字段)。
我需要的是像@course.theoretical_instructor_ids = [1, 2]
这样的内容创建了两个Training
个对象,role
属性设置为理论教练
我在考虑基于关系名称after_initialize
和Training
设置role
:theoretical_instructors
的{{1}}回调,但我确实不知道怎么做。有什么建议?我错过了一些观点吗?
谢谢你们!
来自oli-g的编辑1
This question处理类似问题:不同之处在于我在创建新:practical_instructors
时不想构建Trainer
个对象,但我只想关联现有{ {1}}对象为新的Course
。
来自oli-g的编辑2
基于this(5年之前的帖子)和this博客帖子,我以这种方式更改了Trainer
模型:
Course
这段代码让我可以做这样的事情
Course
这是一种可接受的解决方法,即使在我的控制器中我仍然不能只执行class Course < ActiveRecord::Base
has_many :trainings, dependent: :destroy
has_many :theoretical_instructors, through: :trainings, source: :trainer, conditions: ["trainings.role = ?", "Theoretical Instructor"] do
def <<(theoretical_instructor)
Training.send(:with_scope, create: { role: "Theoretical Instructor" }) { self.concat theoretical_instructor }
end
end
accepts_nested_attributes_for :theoretical_instructors
has_many :practical_instructors, through: :trainings, source: :trainer, conditions: ["trainings.role = ?", "Practical Instructor"] do
def <<(practical_instructor)
Training.send(:with_scope, create: { role: "Practical Instructor" }) { self.concat practical_instructor }
end
end
accepts_nested_attributes_for :practical_instructors
end
,但我必须在:001 > c = Course.first
=> #<Course id: 1>
:002 > t1 = Trainer.first
=> #<Trainer id: 1, name: "Tom">
:003 > c.theoretical_instructors << t1
=> #<Trainer id: 1, name: "Tom">
:004 > Training.all
=> [#<Training id: 1, role: "Theoretical Instructor", trainer_id: 1, course_id: 1>]
和{{1}上迭代创建@course = Course.new(params[:course])
个对象}。
但我很好奇,所以问题仍然存在:我可以做些什么才能让Training
与params[:course][:theoretical_instructor_ids]
一起构建params[:course][:practical_instructor_ids]
个对象?
现在......我想我在Rails中发现了一个错误:
@course = Course.new(params[:course])
我想我会在github问题上报告这个......
编辑3 by oli-g
报告错误答案 0 :(得分:1)
您的问题是,在创建记录之后,您将无法添加关联。在这种情况下,使用课程记录ID存储培训关联,并且直到首次保存课程之后才定义课程ID。您要做的是在创建记录后使用after_create回调来调用函数。
将其添加到课程模型的末尾:
# Use attr accessors to store the initial values so they won't conflict with the *_instructor_ids methods defined above
attr_accessor :create_theoretical_instructors
attr_accessor :create_practical_instructors
# This will call the create_training_records function after the record is created
after_create :create_training_records
private
def create_training_records
create_theoretical_instructors.each do |instructor_id|
self.theoretical_instructors << Instructor.find(instructor_id)
end
create_practical_instructors.each do |instructor_id|
self.practical_instructors << Instructor.find(instructor_id)
end
save!
end
并在视图中更改表单以使用新的attr_accessors:
<%= course_form.label :create_theoretical_instructors %><br />
<%= course_form.select :create_theoretical_instructors, Trainer.all.map { |x| [[x.name, x.surname].join(" "), x.id] }, { }, { multiple: true } %>
<%= course_form.label :create_practical_instructors %><br />
<%= course_form.select :create_practical_instructors, Trainer.all.map { |x| [[x.name, x.surname].join(" "), x.id] }, { }, { multiple: true } %>
现在,当您提交表单时,它会将指导者ID写入新的Course实例变量;在课程经过验证和保存后,它将自动创建新的关联。