使用Rails使用has_many:through和belongs_to正确建模

时间:2011-07-19 02:55:01

标签: ruby-on-rails-3 activerecord

我正在努力寻找为团队/成员/人定义模型的最佳方式,其中一个人可以是许多团队的成员,团队可以拥有许多成员。与“成员”关系一起是该人填补团队的位置。一支球队也应该只有一名主教练和一名助理教练。一个人可以成为多个团队的主管/助理教练。

以下是我目前的(徒劳)尝试:

class Team < ActiveRecord::Base
  has_many :members
  has_many :people, :through => :members
  belongs_to :head_coach :class => 'Person' 
  belongs_to :assistant_coach :class => 'Person'
end

class Person < ActiveRecord::Base
  has_many :teams
  has_many :teams, :through => :members
end

class Member < ActiveRecord::Base
  belongs_to :team
  belongs_to :person
  # has a "position" which is a string
end

这种方法给我带来两个问题:

  1. 团队的belongs_to:head_coach和:assistant_coach不起作用。也许它应该是一个has_one,但是我不确定将belongs_to置于Person中是否有意义(我想在Team to Person中使用FK)。下面的示例显示我如何设置它与ActiveRecord不一致:

    irb(main):006:0> t = Team.find(1)
    => #<Team id: 1, name: "Champs", created_at: "2011-07-18 01:50:56", updated_at: "2011-07-19 01:47:26", head_coach: nil> 
    irb(main):007:0> t.head_coach
    => nil
    irb(main):008:0> t.head_coach = Person.find(1)
    => #<Person id: 1, name: "Chris", created_at: "2011-07-18 01:52:34", updated_at: "2011-07-18 01:52:34">
    irb(main):009:0> t.save
    => true
    irb(main):010:0> t.head_coach
    => #<Person id: 1, name: "Chris", created_at: "2011-07-18 01:52:34", updated_at: "2011-07-18 01:52:34">
    irb(main):011:0> Team.find(1).head_coach
    => nil
    
  2. has_many:through似乎有效,但我没有找到一个很好的方法来列出团队中每个人的位置。这是我目前在视图中的尝试:

    <% @team.people.each do |person| %>
      <%= person.name +" "+ @team.members.find_by_person_id(person).position %>
    
  3. 是否有更好的方法来表示这些关系?

    感谢您的帮助!

    -Chris

2 个答案:

答案 0 :(得分:0)

你的成员表中是否有head_coach和assistant_coach的布尔字段,并使用带范围的验证唯一规则。

EG 添加到成员表

is_head_coach:boolean
is_assistant_coach:boolean 

现在你的会员模型中有

validates_uniqueness_of :is_head_coach, :scope => [:team_id, :person_id]
validates_uniqueness_of :is_assistant_coach, :scope => [:team_id, :person_id]

然后,您可以在个人模型或团队模型中使用一些命名范围来查找head_coach。

至于列出所有职位,你不能使用:

@team.members.each do |member|
   "#{member.person.name} #{member.position}"

如果你想以特定的方式对它们进行排序,你可以在member表中添加一个名为sort_order:integer的字段并按其排序。

更新

我认为上述解决方案不具备可扩展性。如果您在成员模型中使用位置字段并为其创建一个名为“命名范围”的范围,如下所示:

scope :head_coach, lambda { where('position = 'head coach') }

然后你可以使用类似的东西:

team.members.head_coach

你可以进一步了解这个范围

scope :get_position, lambda{|pos| where('position = ?', pos)}

并使用

team.members.get_position('head_coach')

您可以编写一个自定义验证方法来检查您是否最终在每个团队的head_coach上等等。

答案 1 :(得分:0)

我最终使用显式的foreign_key进行以下方法,该方法正常工作。

class Team < ActiveRecord::Base
  has_many :members
  has_many :people, :through => :members
  belongs_to :head_coach, :class => 'Person', :foreign_key head_coach_id 
  belongs_to :assistant_coach, :class => 'Person', :foreign_key assistant_coach_id
end

class Person < ActiveRecord::Base
  has_many :teams, :through => :members

end

class Member < ActiveRecord::Base
  belongs_to :team
  belongs_to :person
  # has a "position" which is a string
end

迄今为止唯一的缺点是我无法在控制器中自动加载外键。我需要添加以下代码以保存关联:

# POST /teams
def create
  @team = Team.new
  @team.head_coach = Person.find(params[:head_coach])
  @team.assistant_coach = Person.find(params[:assistant_coach])

  ....