Понимание: исходная опция has_one / has_many через Rails

184

Пожалуйста, помогите мне понять :sourceвариант has_one/has_many :throughобъединения. Объяснение Rails API для меня мало что значит.

«Определяет имя источника ассоциации используется has_many :through => :queries. Только использовать его , если имя не может быть выведено из ассоциации. has_many :subscribers, :through => :subscriptionsБудет искать либо :subscribersили :subscriberна Subscription, если только :sourceне дано.»

Три Вуонг
источник

Ответы:

238

Иногда вы хотите использовать разные имена для разных ассоциаций. Если имя, которое вы хотите использовать для ассоциации в модели, не совпадает с именем в :throughмодели, вы можете :sourceуказать его.

Я не думаю, что вышеприведенный абзац намного яснее, чем в документах, так что вот пример. Предположим, у нас есть три модели Pet, Dogи Dog::Breed.

class Pet < ActiveRecord::Base
  has_many :dogs
end

class Dog < ActiveRecord::Base
  belongs_to :pet
  has_many :breeds
end

class Dog::Breed < ActiveRecord::Base
  belongs_to :dog
end

В этом случае мы выбрали пространство имен Dog::Breed, потому что хотим получить доступ к ним Dog.find(123).breedsкак к удобной и удобной ассоциации.

Теперь, если мы теперь хотим создать has_many :dog_breeds, :through => :dogsассоциацию Pet, у нас внезапно возникает проблема. Rails не сможет найти :dog_breedsассоциацию Dog, поэтому Rails не может знать, какую Dog ассоциацию вы хотите использовать. Введите :source:

class Pet < ActiveRecord::Base
  has_many :dogs
  has_many :dog_breeds, :through => :dogs, :source => :breeds
end

С помощью :source, мы говорим Rails искать ассоциацию, вызываемую :breedsдля Dogмодели (так как эта модель используется :dogs), и использовать ее.

vonconrad
источник
2
Я думаю, вы хотели, чтобы ваш последний класс Animal был назван классом Pet, просто опечатка, я верю.
Kamilski81
3
В приведенном выше примере, должна ли ассоциация Dogбыть has_many :breedвместо, :breedsа затем :sourceбыть :breedединственной, чтобы представлять имя модели, а не :breedsпредставлять имя таблицы? Например has_many :dog_breeds, :through => :dogs, :source => :breed(без sсуффикса :breed)?
LazerSharks
1
Я проверял это. это единственное число, без sсуффикса в:source =>
Anwar
«В этом случае мы выбрали пространство имен Dog :: Breed, потому что хотим получить доступ к Dog.find (123) .breeds как к удобной и удобной ассоциации». Вам не нужно пространство имен для этого?
Jwan622
201

Позвольте мне расширить этот пример:

class User
  has_many :subscriptions
  has_many :newsletters, :through => :subscriptions
end

class Newsletter
  has_many :subscriptions
  has_many :users, :through => :subscriptions
end

class Subscription
  belongs_to :newsletter
  belongs_to :user
end

С помощью этого кода вы можете сделать что-то вроде Newsletter.find(id).usersполучения списка подписчиков на рассылку. Но если вы хотите быть более понятным и иметь возможность печатать Newsletter.find(id).subscribersвместо этого, вы должны изменить класс Newsletter на этот:

class Newsletter
  has_many :subscriptions
  has_many :subscribers, :through => :subscriptions, :source => :user
end

Вы переименовываете usersассоциацию в subscribers. Если вы не предоставите :source, Rails будет искать ассоциацию, вызываемую subscriberв классе Subscription. Вы должны указать ему использовать userассоциацию в классе подписки, чтобы составить список подписчиков.

Джереми Рутен
источник
2
обратите внимание, что имена моделей в единственном числе должны использоваться :source =>, а не во множественном числе. Итак, :usersэто неправильно, :userэто правильно
Анвар
Это лучший ответ! Позвольте мне подчеркнуть эту часть: «Вы переименовываете ассоциацию пользователей в подписчики. Если вы не предоставите: source, Rails будет искать ассоциацию, называемую подписчиком, в классе Subscription».
Брайан Джозеф Спинос
11

Самый простой ответ:

Имя отношения в таблице в середине.

Dreeub
источник