与同一模型的多个关联

时间:2010-02-08 04:24:42

标签: ruby-on-rails activerecord

我有两个类,我想指定如下:

class Club < ActiveRecord::Base
  belongs_to :president, :class_name => "Person", :foreign_key => "president_id"
  belongs_to :vice_president, 
             :class_name => "Person",
             :foreign_key => "vice_president_id"
end

class Person < ActiveRecord::Base
  has_one :club, :conditions => 
  ['president_id = ? OR vice_president_id = ?', '#{self.id}', '#{self.id}']
end

当尝试从人物对象获取俱乐部关联时,这不起作用并且给我一个错误。错误是因为我在查看SQL时在俱乐部表中查找person_id。我可以通过声明多个has_one关联来解决它,但感觉这是不正确的做法。

一个人只能是一个俱乐部的总裁或副总裁。

任何能够就此问题提供一些建议的人,我都会非常感激。

4 个答案:

答案 0 :(得分:8)

据我所知,你的has_one条件在Rails中永远不会有效。

两个表都需要一个明确的has_onebelongs_to或has_many per“link”。因此,如果您有两个“链接”,则需要两个has_one和两个belongs_to。这就是它的工作原理。

其次,我认为你应该重新考虑你的模型。你这样做的方式,一个人不能同时成为俱乐部和员工的总裁。或者是两个俱乐部的总裁。即使你现在没有这些,它们也可以在未来出现 - 现在更容易保持灵活性。

一种灵活的方法是使用has_many :through和一个指定角色的中间表。换句话说:

# The memberships table has a person_id, club_id and role_id, all integers

class Membership < ActiveRecord::Base
  belongs_to :club
  belongs_to :person
  validates_presence_of :role_id
  validates_numericality_of :role_id
end

class Club < ActiveRecord::Base
  has_many :memberships, :dependent => :delete_all
  has_many :people, :through => :memberships
end

class Person < ActiveRecord::Base
  has_many :memberships, :dependent => :delete_all
  has_many :clubs, :through => :memberships
end

现在,假设role_id = 0表示员工,role_id = 1表示总裁,而role_id = 2表示vice_president,您可以这样使用:

tyler = Person.find(1) # person_id is 1
other = Person.find(2) # person_id is 2
c = Club.find(1)  # club_id is 1

tyler.clubs # returns all the clubs this person is "member" of
c.people # returns all the "members" of this club, no matter their role

#make tyler the president of c
tyler.memberships.create(:club_id => 1, :role_id => 1)

#make other the vicepresident of c
#but using c.memberships instead of other.memberships (works both ways)
c.memberships.create(:person_id => 2, :role_id => 1)

#find the (first) president of c
c.memberships.find_by_role_id(1).person

#find the (first) vicepresident of c
c.memberships.find_by_role_id(2).person

#find all the employees of c
c.memberships.find_all_by_role_id(0).collect { |m| m.person }

#find all the clubs of which tyler is president
tyler.memberships.find_all_by_role_id(1).collect { |m| m.club }

附加说明:

  • 您可以使用角色表和模型对此进行补充。角色只有一个名称,角色将have_many关系和成员资格belong_to角色。或者,您可以在成员资格中定义获取角色名称的方法(如果为0,则返回“employee”,如果为1,则为“president”等
  • 您可以在会员身上添加验证,这样就可以让不超过1人成为某个俱乐部的主席,或同一个俱乐部的同一个员工两次。稍后,如果您开始获得“特殊情况”,其中一个人需要在两个地方,您只需要调整您的验证。

答案 1 :(得分:0)

我认为你的联想是错误的。你的方式很难指派总统或副总统。

我会这样做:

class Club < ActiveRecord::Base
  has_one :president, 
          :class_name => "Person",
          :foreign_key => 'president_club_id'
  has_one :vice_president, 
          :class_name => "Person", 
          :foreign_key => 'vice_president_club_id'
end

class Person < ActiveRecord::Base
  belongs_to :club
end

现在您可以分配如下角色:

club.president = Person.create(:name => 'Tom')
club.vice_president = Person.create(:name => 'Andrew')

答案 2 :(得分:0)

我建议你介绍一个叫做角色的新模型。然后有以下内容:

class Club
  has_many :roles 

  def president
  end

  def vice_president
  end

end

class Person
  belongs_to :role
end

class Role
  has_one :person
  belongs_to :club
end

答案 3 :(得分:0)

这是多态关联的经典用例。 链接在这里: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

像...这样的东西。

class Club < ActiveRecord::Base
  belongs_to :person, :polymorphic => true 

class Person < ActiveRecord::Base
  has_one :club, :as => :position