加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ruby-on-rails-3 – Rails 3和has_many:through:在连接模型上

发布时间:2020-12-17 01:59:47 所属栏目:百科 来源:网络整理
导读:我深入搜索了网页,以便找到一个干净简单的方法来处理has_many:through关系的连接模型上的属性初始化,但我没有找到满足我需要的最佳解决方案. 在下面提供的exaple中,我需要在创建或更新Course对象时自动设置Training join模型的属性角色. 这是我的模特: QUA
我深入搜索了网页,以便找到一个干净简单的方法来处理has_many:through关系的连接模型上的属性初始化,但我没有找到满足我需要的最佳解决方案.

在下面提供的exaple中,我需要在创建或更新Course对象时自动设置Training join模型的属性角色.

这是我的模特:

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,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连接模型(可能爆炸表的数量)来解决这个问题.

此视图提供了提交新课程的表单:

<%= 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,{ multiple: true } %>

  <%= course_form.submit %>
<% end%>

问题是:为了使@course = Course.new(params [:course])能够在课程控制器中唯一的代码行在提交上一个表单时保存这个关联,我能做些什么?

与this question不同,我不想在创建新课程时创建新的Trainer对象:我想从已经存在于DB中的那些(通过多选输入字段)中选择它们.

我需要的是像@ course.theoretical_instructor_ids = [1,2]之类的东西创建两个Training对象,其role属性设置为Theory Instructor

我正在考虑关于训练的after_initialize回调,它基于关系名称(:theoretical_instructors和:practical_instructors)设置角色,但我真的不知道该怎么做.有什么建议?我错过了一些观点吗?

感谢你们!

来自oli-g的编辑1

This question处理类似的问题:区别在于我不想在创建新课程时构建Trainer对象,但我只想将现有的Trainer对象与新课程相关联.

编辑2来自oli-g

基于this(5岁的帖子)和this博客文章,我以这种方式更改了课程模型:

class Course < ActiveRecord::Base
  has_many :trainings,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,"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>]

这是一个可接受的解决方法,即使在我的控制器中我仍然不能只做@course = Course.new(params [:course]),但我必须创建训练对象迭代params [:course] [:theoretical_instructor_ids]和params [:course] [:practical_instructor_ids].

但我很好奇,所以问题仍然存在:为了使@course = Course.new(params [:course])能够与课程一起构建训练对象,我能做些什么?

现在……我想我在Rails中发现了一个错误:

:005 > c.practical_instructors
=> []        # correct
:006 > c.practical_instructor_ids
=> []        # obviously
:007 > c.reload
=> #<Course id: 1>
:008 > c.practical_instructor_ids
=> [1]       # WRONG!!!
:009 > c.practical_instructors
=> []        # now it's correct...
:010 > c.practical_instructor_ids
=> []        # WTF!?

我想我会在github问题上报告这个……

编辑3由oli-g

Bug报道于github

解决方法

您的问题是,在创建记录之前,您将无法添加关联.在这种情况下,使用课程记录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,{ multiple: true } %>

<%= course_form.label :create_practical_instructors %><br />
<%= course_form.select :create_practical_instructors,{ multiple: true } %>

现在,当您提交表单时,它会将指导者ID写入新的Course实例变量;在课程经过验证和保存后,它将自动创建新的关联.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读