Rails - 建模多重多对多关系

3
我有一个关于处理课程的应用程序的使用案例,具体如下:
  1. 11/1在Bos由Curt教授A班
  2. 10/19在NY由Curt教授A班
  3. 12/5在SF由Jane教授A班
  4. 11/1在Bos由Jane教授A班
针对这个应用程序,创建多对多关系的最佳方式是什么?
是否应该为该应用程序创建一个“teachings”模型,该模型属于课程、教师和位置,并带有日期列?
2个回答

2

你已经在正确的道路上。这是我如何建模这些关系的方法。假设你有一个Teacher模型,一个Course模型和一个TeacherCourses模型,它将是教师和课程之间的连接表:

class Teacher < ActiveRecord::Base
 has_many :courses, through: :teacher_courses
end

class Course < ActiveRecord::Base
 has_many :teachers, through: :teacher_courses
end

class TeacherCourse < ActiveRecord::Base
  belongs_to :course
  belongs_to :teacher
end

您的teacher_courses表还应该有一个位置属性,以区分相同课程/教师组合的记录:

create_table :teacher_courses do |t|
  t.integer :teacher_id
  t.integer :course_id
  t.string :location
  t.timestamps
end

我仍然会使用单独的表格来存储位置信息以进行数据规范化。 - max
不太确定我是否理解了。那会有什么好处呢?我相信你是对的,只是想知道这种权衡的利弊。 - Cyzanfar
1
不要再使用数百个带有不同变体的字符串来表示“D-Wing,Derpville大学,Derpville,DERPLAND”,而是指向单个规范化记录。这还使得添加例如地理位置等功能变得简单明了。 - max
@Cyzanfar 目的是能够重复使用位置,因为它可以有许多课程。您的解决方案似乎表明每次创建课程时都必须输入位置。 - Arash Hadipanah

2
您想要的是为每个实体创建一个模型:

  • 课程
  • 教师
  • 地点

然后您可以创建一种联接模型,我选择称之为“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

我一直在使用这个结构来创建模型,但我发现当使用fields_for :locations和fields_for :instructors提交课程时,关联表会为course_id + instructor_id、course_id + location_id创建两个单独的条目,我期望只有一个条目包含course_id、instructor_id和location_id。你认为可能是什么原因导致了这种情况?
当隐式地创建join model时,ActiveRecords只会跟踪一个关联。要进行三方连接,您需要显式地创建join model。
<%= 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_foraccepts_nested_attributes是强大的工具。然而,嵌套多层级的属性传递被视为某种反模式,因为它会创建神类和意想不到的复杂性。

更好的替代方案是使用AJAX发送单独的请求来创建教师和位置。这样做可以提供更好的用户体验,减少验证问题,并改善应用程序设计。


谢谢您,我一直在为模型玩弄这个结构,但我注意到当使用fields_for:locationsfields_for:instructors提交课程时,associations表会创建两个独立的条目,一个是course_id+instructor_id,另一个是course_id+location_id。我期望的是单个course_id,instructor_id,location_id的条目。您认为可能是什么原因导致这种情况发生? - Arash Hadipanah
因为你做错了。当连接模型不仅仅是双向连接模型时,你无法隐式地创建它,因为ActiveRecord只能跟踪一个关系。请参见我的编辑。 - max
谢谢提供信息,我之前不知道Rails在三表联接方面有限制。所以唯一的方法就是分别创建位置和教师,然后使用集合选择器将它们关联起来? - Arash Hadipanah
是的,您可以将 fields_for(:teachers) 嵌套在 fields_for(:lessons) 内部,但这是一种反模式。 - max
有趣的建议,我会尝试将其变成一个三步骤的过程。关于包含每个课程的日期,您建议在课程表中添加一列吗? - Arash Hadipanah
做得好,我现在明白了将它们分开成离散的对象,然后相互关联的重点。感谢大家的反馈! - Cyzanfar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接