Відповіді:
Скажімо, у вас дві моделі: 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
user.groups << group
? Або все, чим займається ця асоціація?
Скажіть, у вас є ці моделі:
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
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: через, якщо вам потрібні перевірки, зворотні дзвінки або додаткові атрибути на моделі приєднання.
user
моделі, щоб додати групу. Щось на зразок редакції, яку я щойно зробив. Сподіваюся, це допомагає.