Rails Ассоциация ActiveRecord - PullRequest
       24

Rails Ассоциация ActiveRecord

2 голосов
/ 06 марта 2011

Хорошо, вот мой вопрос.У меня есть 3 разных модели: люди, роли, клиент и магазин.Клиенты имеют много магазинов, а также могут иметь много людей.В магазинах много людей.У людей разные роли.1 человек может работать в нескольких магазинах, и они могут иметь разные роли в каждом магазине.

Например.Джо может быть помощником менеджера в одном магазине и менеджером в другом магазине.То, что я хотел бы сделать, это получить правильные роли, выполнив что-то вроде

Store.find(1).people.find(1).roles
(вернул бы «помощник менеджера», например) или

Store.find(2).people.find(1).roles
(вернул бы «менеджер», например),Можно ли это сделать в ActiveRecord?

Я создал таблицу: role_people со следующим определением:

create_table :roles_people, :id => false do |t|
      t.references :role
      t.references :person
      t.references :store
      t.references :client
end

Однако я не могу понять, как заставить работать ассоциацииправильно используя эту таблицу.Кто-нибудь может указать мне правильное направление?

Спасибо

Ответы [ 5 ]

3 голосов
/ 08 марта 2011
class People
  belongs_to :client
  has_many :store_roles
end

class Roles
  has_many :store_roles
end

class StoreRole
  belongs_to :role
  belongs_to :people
  belongs_to :store
end

class Client
  has_many :stores
  has_many :people
end

class Store
  belongs_to :client
  has_many :store_roles
  has_many :roles, :through => :store_roles
end

Предположим, что все эти классы наследуются от ActiveRecord::Base;)

Вам потребуется настроить миграцию и структуру базы данных, чтобы отразить эти отношения.Для каждого belongs_to есть поле :object_id в таблице, ссылающееся на соответствующий идентификатор таблицы.

Ваш запрос должен выглядеть примерно так:

Store.find(1).roles.find(:all, :conditions => ["store_roles.person_id = ?", 1])

Я бы, наверное,добавьте метод в модель магазина, чтобы сделать это немного проще:

def roles_for(person_id)
  roles.find(:all, :conditions => ["store_roles.person_id = ?", person_id])
end

Таким образом, вы можете найти роли, используя:

Store.find(1).roles_for(1)

Или, еще лучше:

def self.roles_for(store_id, person_id)
  Role.find(:all, :joins => :store_roles, :conditions => ["store_roles.store_id = ? AND store_roles.person_id = ?", store_id, person_id])
end

Что меняет наш искатель на:

Store.roles_for(1, 1)

Я бы сказал, что этот последний метод является наиболее идеальным, поскольку он вызывает только один запрос, в то время как каждый из других параметров выполняет два запроса кпросмотр базы данных по ролям (один для поиска магазина и один для получения ролей для person_id).Конечно, если у вас уже есть экземпляр объекта Store, это не имеет большого значения.

Надеюсь, этого ответа было достаточно:)

1 голос
/ 06 марта 2011

Я думаю, что вы хотите, это has_many: через

class Person < ActiveRecord::Base
  has_many :roles_people
  has_many :roles, :through => :roles_people
end

class Store < ActiveRecord::Base
  has_many :roles_people
  has_many :people, :through => roles_people
end

Вам также нужно будет добавить отношения к RolePerson:

class RolePerson < ActiveRecord::Base
  belongs_to :store
  belongs_to :person
  has_one :role
end

Это то, что вы искали?1007 *

Очень полезная ссылка @blog.hasmanythrough.com

0 голосов
/ 06 марта 2011

Хороший вопрос. Вы не можете делать именно то, что вы хотели, но я думаю, мы можем приблизиться. Для полноты я собираюсь подвести итог вашей структуре данных:

class Client
  has_many :stores
end

class Store
  has_many :people
  has_many :roles
end

class Person
  has_many :roles
  has_many :stores
end

class Role
  belongs_to :store
  belongs_to :person
end

Вы видите, что роли не нужна ссылка на клиента, потому что ее можно найти сразу из хранилища (я предполагаю, что хранимое "принадлежит" только одному клиенту).

Теперь role связан как с person, так и с store, поэтому у человека могут быть разные роли в магазине. И чтобы найти их в чистом виде, я бы использовал вспомогательную функцию:

class Person
  has_many :roles
  has_many :stores

  def roles_for(store)
    roles.where("store_id=?", store.id)
  end
end

Так что вы не можете написать что-то вроде

store.people.first.roles

чтобы получить роли первого человека, работающего в этом магазине. Но писать что-то вроде:

store.people.first.roles_for(store)

не слишком сложно, я надеюсь.

Причина, по которой это так, заключается в том, что в контексте человека (-> store.people.first) у нас больше нет представления о магазине (как мы туда попали).

Надеюсь, это поможет.

0 голосов
/ 06 марта 2011

Вам нужно изменить имя таблицы на people_roles, и вы можете отбросить ссылки как на магазин, так и на клиента:

create_table :roles_people, :id => false do |t|
  t.references :role
  t.references :person
  t.references :store
end

Роль - это то, что принадлежит только людям.

Тогда вынеобходимо использовать has_and_belongs_to_many:

class Person < ActiveRecord::Base
  has_many :roles
  has_many :stores, :through => :people_roles
end

class Store < ActiveRecord::Base
  has_many :roles
  has_many :people, :through => :people_roles
end

, чем вы можете запросить:

Store.find(1).people.find(1).roles
0 голосов
/ 06 марта 2011

has_and_belongs_to_many - ваш друг.

class Person < ActiveRecord::Base
  has_and_belongs_to_many :roles
end

Таким образом, вы можете получить все роли, которые играет этот человек, позвонив Person.roles.all.Полученный запрос будет использовать таблицу people_roles.Вы также можете использовать has_many :through, но вам придется самостоятельно создавать классы моделей для таблицы соединений и поддерживать все ассоциации самостоятельно.Иногда это необходимо, иногда нет.Зависит от сложности вашей фактической модели.

...