Коли використовувати відношення "has_many: through" в Rails?


123

Я намагаюся зрозуміти, що has_many :throughтаке і коли ним користуватися (і як). Однак я цього не отримую. Я читаю Beginning Rails 3 і спробував Google, але я не в змозі зрозуміти.

Відповіді:


177

Скажімо, у вас дві моделі: Userі Group.

Якщо ви хочете, щоб користувачі належали до груп, ви можете зробити щось подібне:

class Group < ActiveRecord::Base
  has_many :users
end

class User < ActiveRecord::Base
  belongs_to :group
end

Що робити, якщо ви хочете відслідковувати додаткові метадані навколо асоціації? Наприклад, коли користувач приєднався до групи або, можливо, яка роль користувача у групі?

Тут ви робите об'єднання об'єктом першого класу:

class GroupMembership < ActiveRecord::Base
  belongs_to :user
  belongs_to :group

  # has attributes for date_joined and role
end

Це вводить нову таблицю та виключає group_idстовпчик із таблиці користувача.

Проблема цього коду полягає в тому, що вам доведеться оновлювати всі місця, де ви користуєтесь класом користувача, і змінювати його:

user.groups.first.name

# becomes

user.group_memberships.first.group.name

Цей тип коду смокче, і внесення змін, подібних до цього, болісно.

has_many :through дає вам найкраще з обох світів:

class User < ActiveRecord::Base
  has_many :groups, :through => :group_memberships  # Edit :needs to be plural same as the has_many relationship   
  has_many :group_memberships
end

Тепер ви можете ставитися до цього як до звичайного has_many, але отримуйте переваги моделі асоціації, коли вам це потрібно.

Зауважте, що ви також можете це зробити has_one.

Редагування: полегшення додавання користувача до групи

def add_group(group, role = "member")
  self.group_associations.build(:group => group, :role => role)
end

2
Тут я б додав метод на userмоделі, щоб додати групу. Щось на зразок редакції, яку я щойно зробив. Сподіваюся, це допомагає.
Бен Шейрман

Я бачу, чи я теж повинен писати user.groups << group? Або все, чим займається ця асоціація?
LuckyLuke

У мене є клас, такий самий, як group_membership, і у мене виникають проблеми з використанням фабричної дівчини з ним, допоможеш мені?
Айман Салах

Я б використовував "has_many: group_memberships". Використання однини буде спрацьовувати, але user.group_membership буде колекцією, а user.group_membership не працюватиме.
каласир

Це був друкарський помилок. Виправлено.
Бен Шейрман

212

Скажіть, у вас є ці моделі:

Car
Engine
Piston

Автомобіль has_one :engine
Двигун belongs_to :car
Двигун has_many :pistons
Поршеньbelongs_to :engine

has_many :pistons, through: :engine
Поршень автомобіляhas_one :car, through: :engine

По суті, ви делегуєте відносини моделі до іншої моделі, тому замість того, щоб дзвонити car.engine.pistons, ви можете просто зробитиcar.pistons


9
Це має багато сенсу!
RajG

24
Я називаю це SACA - ShortAndClearAnswer.
Asme Тільки

18

ActiveRecord Приєднати таблиці

has_many :throughі has_and_belongs_to_manyвідносини функціонують через таблицю приєднання , яка є проміжною таблицею, яка представляє зв'язок між іншими таблицями. На відміну від запиту JOIN, дані фактично зберігаються в таблиці.

Практичні відмінності

З цим has_and_belongs_to_manyвам не потрібен первинний ключ, і ви отримуєте доступ до записів через зв'язки ActiveRecord, а не через модель ActiveRecord. Зазвичай ви використовуєте HABTM, коли хочете з'єднати дві моделі у відносинах "багато-багато".

Ви використовуєте has_many :throughвідносини, коли хочете взаємодіяти з таблицею з'єднання як модель Rails, доповнену первинними ключами та можливістю додавати власні стовпці до об'єднаних даних. Останнє особливо важливо для даних, що стосуються об'єднаних рядків, але насправді не належать до пов'язаних моделей - наприклад, для зберігання обчисленого значення, отриманого з полів в об'єднаному рядку.

Дивитися також

У посібнику з Асоціацій активного запису в цій рекомендації написано:

Найпростіше правило: вам слід створити has_many: через взаємовідносини, якщо вам потрібно працювати з моделлю відносин як незалежною сутністю. Якщо вам не потрібно нічого робити з моделлю відносин, можливо, простіше встановити відносини has_and_belongs_to_many (хоча вам потрібно пам'ятати, щоб створити таблицю приєднання в базі даних).

Ви повинні використовувати has_many: через, якщо вам потрібні перевірки, зворотні дзвінки або додаткові атрибути на моделі приєднання.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.