Rails has_one: через ассоциацию

В Rails есть has_one :through ассоциация, которая помогает установить однозначную ассоциацию с третьей моделью, проходя через вторую модель. Какая реальная польза от этого, помимо создания быстрой ассоциации, в противном случае это был бы дополнительный шаг.

Взяв этот пример из руководства Rails:

class Supplier < ActiveRecord::Base
  has_one :account
  has_one :account_history, :through => :account
end

class Account < ActiveRecord::Base
  belongs_to :supplier
  has_one :account_history
end

class AccountHistory < ActiveRecord::Base
  belongs_to :account
end

может позволить нам сделать что-то вроде:

supplier.account_history

которые в противном случае были бы достигнуты как:

supplier.account.history

Если это только для более простого доступа, то технически может существовать однозначная ассоциация, которая связывает модель с некоторой n-й моделью, проходящей через n-1 модели для облегчения доступа. Есть ли что-то еще, чего мне не хватает, кроме ярлыка?


person Community    schedule 22.01.2010    source источник
comment
СПАСИБО ОГРОМНОЕ. Я боролся пару дней, пытаясь заставить это работать, не зная, что мне нужна директива: through. Теперь все лучше.   -  person ed_is_my_name    schedule 01.11.2020


Ответы (3)


  1. Логика. Хорошо, это может показаться немного слабым для этого, но было бы логично сказать, что «У меня есть поставщик, у которого есть учетная запись у меня, я хочу увидеть всю историю учетной записи этого поставщика» , поэтому для меня имеет смысл иметь доступ к истории аккаунта напрямую от поставщика.

  2. Эффективность: это для меня основная причина, по которой я бы использовал :through, просто потому, что это выдает оператор соединения, а не вызов поставщика, а затем account, а затем account_history. заметил количество обращений к базе данных?

    • с использованием :through, 1 вызов для получения поставщика, 1 вызов для получения account_history (rails автоматически использует :join для получения через учетную запись)

    • с использованием обычной ассоциации, 1 вызов для получения поставщика, 1 вызов для получения учетной записи и 1 вызов для получения account_history

Вот что я думаю =) надеюсь поможет!

person Community    schedule 22.01.2010
comment
Я думаю, что логический аргумент вполне уместен. Более естественным будет сказать: дайте мне историю счетов этого поставщика, а не историю счетов поставщика. Очень тонко, но все же легче запомнить, учитывая философию Ruby / Rails, касающуюся плавных предложений, а не кода. Я знаю, что мы можем видеть фактические запросы к БД, но указывает ли Rails, как эти вызовы методов будут преобразовываться в SQL? - person Anurag; 22.01.2010
comment
Это также позволяет избежать нарушения Закона Деметры. - person Tom Crayford; 16.02.2011
comment
@TomCrayford, я действительно не понимаю, как это происходит. Не делает ли это отношение менее прямым? - person Jasper Kennis; 20.05.2012
comment
@JasperKennis, физическая связь менее прямая, но если предположить другие причины, по которым таблицы не связываются напрямую (например, денормализация supplier_id из учетной записи в account_history требует обратного вызова и небольшого количества дополнительного места для хранения), это делает так, что вы не Не нужно писать цепочку методов каждый раз, когда вы получаете доступ к истории аккаунта через поставщика. И если таблица между ними изменится позже, вам не придется переписывать вызовы методов во всем приложении. Вам нужно только изменить ассоциацию (и). - person maurice; 06.08.2015
comment
Давайте также не будем забывать об избежании сценария, когда вам может потребоваться вызвать другой метод класса изнутри класса для очень простой операции, и вы не хотите создавать интерфейс или объект службы для того, что может быть чрезвычайно простым запросом данных. has_one :through намного чище для выполнения операций с третичными реляционными данными, чем внедрение или создание экземпляра третичного класса в вызывающем классе. - person Todd; 09.12.2015

  • Обратная связь: рассмотрим классическую ситуацию пользователь-членство-группа. Если пользователь может быть участником многих групп, то в группе много участников или пользователей, а у пользователя много групп. Но если пользователь может быть членом только одной группы, в группе все равно будет много участников: class User has_one :group, :through => :membership, но class Group has_many :members, :through => memberships. Промежуточная модель membership полезна для отслеживания обратной зависимости.

  • Расширяемость: has_one :through отношения можно легко расширить и расширить до has_many :through.

person Community    schedule 02.04.2013

Я удивлен, что никто не коснулся объектов ассоциации.

Отношение has_many (или has_one) :through облегчает использование шаблон объекта связи, который возникает, когда у вас есть две вещи, связанные друг с другом, и это отношение само имеет атрибуты (т. е. дата, когда ассоциация была создана или когда истекает срок ее действия).

Это рассматривается некоторыми как хорошая альтернатива has_and_belongs_to_many помощнику ActiveRecord. Причина этого заключается в том, что очень вероятно, что вам нужно будет изменить характер ассоциации или добавить к ней, и когда вы находитесь в течение пары месяцев в проекте, это может быть очень болезненным, если отношения изначально были установлены как a has_and_belongs_to_many (вторая ссылка содержит подробности). Если она изначально настроена с использованием отношения has_many :through, то через пару месяцев после начала работы над проектом модель соединения легко переименовать или добавить к ней атрибуты, что упростит разработчикам реагирование на меняющиеся требования. План перемен.

person Community    schedule 09.09.2014