Я пытаюсь понять, что это has_many :through
такое и когда его использовать (и как). Однако я этого не понимаю. Я читаю Beginning Rails 3 и пробовал поискать в Google, но не могу понять.
Я пытаюсь понять, что это has_many :through
такое и когда его использовать (и как). Однако я этого не понимаю. Я читаю Beginning Rails 3 и пробовал поискать в Google, но не могу понять.
Ответы:
Допустим, у вас есть две модели: 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, в комплекте с первичными ключами и возможностью добавлять настраиваемые столбцы к объединенным данным. Последнее особенно важно для данных, которые относятся к объединенным строкам, но на самом деле не относятся к связанным моделям - например, для хранения вычисленного значения, полученного из полей в объединенной строке.
В Руководстве по ассоциациям Active Record рекомендация гласит:
Простейшее практическое правило состоит в том, что вы должны настроить связь has_many: through, если вам нужно работать с моделью отношений как с независимым объектом. Если вам не нужно ничего делать с моделью отношений, может быть проще установить связь has_and_belongs_to_many (хотя вам нужно не забыть создать таблицу соединения в базе данных).
Вы должны использовать has_many: through, если вам нужны проверки, обратные вызовы или дополнительные атрибуты в модели соединения.
user
модель метод для добавления группы. Что-то вроде той правки, которую я только что сделал. Надеюсь это поможет.