【问题标题】:Using Rails to properly model with has_many :through and belongs_to使用 Rails 正确建模 has_many :through 和 belongs_to
【发布时间】:2011-07-19 02:55:00
【问题描述】:

我正在尝试找到为团队/成员/个人定义模型的最佳方式,其中一个人可以是多个团队的成员,一个团队可以有多个成员。与“成员”关系一起的是该人为团队担任的职位。一支球队也应该只有一名主教练和一名助理教练。一个人可以是多个团队的主教练/助理教练。

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

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中是否有意义(我想要一个FK in Team to Person)。下面的示例显示我的设置方式与 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 %>
    

是否有更好的方法来表示这些关系?

感谢您的帮助!

-克里斯

【问题讨论】:

    标签: ruby-on-rails-3 activerecord


    【解决方案1】:

    我最终采用了以下方法,使用了可以正常工作的显式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])
    
      ....
    

    【讨论】:

      【解决方案2】:

      您能否在 members 表中为 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}"
      

      如果您想以特定方式对它们进行排序,也许您可​​以在成员表中添加一个名为 sort_order:integer 的字段并按此排序。

      更新:

      我认为上述解决方案不可扩展。如果您在成员模型中使用 position 字段并为它创建一个范围又称为“命名范围”,如下所示:

      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 等。

      【讨论】:

      • 感谢您的建议。关于第一部分,我打算扩大规模,以便它还有更多(“n”)角色,如投球教练、击球教练、基础教练、经理等......我喜欢范围建议,但我想知道如果我最终担任许多不同的角色,它将如何扩展?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-04
      • 2011-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多