I have the following use cases for creating an app that handles courses;
What's the best way to create models with many to many relationships for this app?
Should the app have a teachings
model that belongs to courses, teachers, and locations with a column for the date?
What you want is to create a model for each entity:
You then create a join model of sorts which I have choosen to call Lesson:
class Course < ActiveRecord::Base
has_many :lessons
has_many :locations, through: :lessons
has_many :teachers, through: :lessons
end
class Lesson < ActiveRecord::Base
belongs_to :course
belongs_to :teacher
belongs_to :location
end
class Teacher < ActiveRecord::Base
has_many :lessons
has_many :courses, through: :lessons
end
class Location < ActiveRecord::Base
has_many :lessons
has_many :courses, through: :lessons
has_many :teachers, through: :lessons
end
I've been playing with this structure for the models but what I noticed is that when submitting the course with a fields_for :locations and a fields_for :instructors, the associations table is creating two separate entries for course_id + instructor_id, course_id + location_id, I would expect a single entry for course_id, instructor_id, location_id. Any thoughts as to why that might happen?
ActiveRecords only ever keeps track of one assocation when you create join models implicitly. To do three way joins you need to create the join model explicitly.
<%= form_for(@course) do |f| %>
<div class="field>
<% f.label :name %>
<% f.text_field :name %>
</div>
<fieldset>
<legend>Lesson plan<legend>
<%= f.fields_for(:lessons) do |l| %>
<div class="field>
<% l.label :name %>
<% l.text_field :name %>
</div>
<div class="field">
<% l.label :starts_at %>
<% l.datetime_select :starts_at %>
</div>
<div class="field">
<% l.label :teacher_ids %>
<% l.collection_select :teacher_ids, Teacher.all, :id, :name, multiple: true %>
</div>
<div class="field">
<% l.label :location_id %>
<% l.collection_select :location_id, Location.all, :id, :name %>
</div>
<% end %>
</fieldset>
<% end %>
fields_for
and accepts_nested_attributes
are powerful tools. However passing attributes nested several levels down can be seen as an anti-pattern of sorts since it creates god classes and unexpected complexity.
A better alternative is to use AJAX to send separate requests to create teachers and locations. It gives a better UX, less validation headaches and better application design.