Я глубоко искал в Интернете, чтобы найти чистый и простой способ справиться с инициализацией атрибутов в модели соединения 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
, необходимой для сохранения этой ассоциации при отправке предыдущей формы?
В отличие от этого вопроса Я не хочу создавать новые Trainer
объекты при создании нового Course
: я хочу выбирать их из тех, которые уже присутствуют в БД (через мультиселекторполе ввода).
Мне нужно, чтобы что-то вроде @course.theoretical_instructor_ids = [1, 2]
создавало два Training
объекта с атрибутом role
, установленным в Теоретический инструктор
I 'Я думаю о after_initialize
обратном вызове Training
, который устанавливает role
на основе имени отношения (:theoretical_instructors
и :practical_instructors
), но я действительно не знаю, как это сделать.Любой совет?Я что-то упустил?
Спасибо, ребята!
РЕДАКТИРОВАТЬ 1 из oli-g
Этот вопрос имеет отношение кпохожая проблема: разница в том, что я не хочу создавать Trainer
объекты при создании нового Course
, но я просто хочу связать существующие Trainer
объекты с новым Course
.
РЕДАКТИРОВАТЬ 2 от oli-g
Основываясь на этом (5-летнем посте) и этом сообщениях в блоге, я имеюизменил модель 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>]
Это приемлемый обходной путь, даже если в моем контроллере явсе еще не могу сделать только @course = Course.new(params[:course])
, но мне нужно создать Training
объектов, повторяющихся на params[:course][:theoretical_instructor_ids]
и params[:course][:practical_instructor_ids]
.
Но мне любопытно, поэтому вопрос остается открытым: что я могу сделатьчтобы разрешить @course = Course.new(params[:course])
строить Training
объекты вместе с 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
Ошибка сообщена в github