Несколько ассоциаций с одной моделью - PullRequest
6 голосов
/ 08 февраля 2010

У меня есть два класса, которые я хотел бы указать следующим образом:

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

Это не работает и выдает ошибку при попытке получить ассоциацию клуба от объекта person. Ошибка в том, что ищет person_id в клубной таблице, когда я смотрю на SQL. Я могу обойти это, объявив несколько ассоциаций has_one, но чувствую, что это неправильный способ сделать это.

Человек может быть президентом или вице-президентом только одного клуба.

Любой, кто сможет дать небольшой совет по этому вопросу, был бы очень признателен.

Ответы [ 4 ]

8 голосов
/ 10 февраля 2010

Насколько я знаю, ваше условие has_one никогда не будет работать в Rails.

Вам нужно одно явное has_one или belongs_to или has_many на каждую "ссылку" в обеих таблицах. Так что если у вас есть две «ссылки», вам нужно две 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 означает вице-президента, вы можете использовать его следующим образом:

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, он возвращает «сотрудник», если 1, «президент» и т. Д.
  • Вы можете добавить проверки членства, чтобы не более 1 человека мог быть назначен президентом данного клуба, или один и тот же сотрудник в одном клубе дважды. Позже, если вы начнете получать «исключительные случаи», когда человек должен находиться в двух местах, вам просто придется адаптировать свои проверки.
0 голосов
/ 08 февраля 2010

Предлагаю вам представить новую модель под названием роль. Тогда имейте следующее:

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
0 голосов
/ 08 февраля 2010

Это классический вариант использования для полиморфных ассоциаций. Вот ссылка: 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
0 голосов
/ 08 февраля 2010

Я думаю, что ваши ассоциации неправильные. По-твоему, трудно назначить президента или вице-президента.

Я бы сделал это так:

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')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...