Entendendo: opção de origem has_one / has_many através do Rails

184

Por favor, ajude-me a entender a :sourceopção de has_one/has_many :throughassociação. A explicação da API do Rails faz muito pouco sentido para mim.

"Especifica o nome da associação de origem usado por has_many :through => :queries. Apenas use-o se o nome não puder ser deduzido da associação. has_many :subscribers, :through => :subscriptionsProcurará por um :subscribersou :subscriberpor outro Subscription, a menos que :sourceseja fornecido um."

Tri Vuong
fonte

Respostas:

238

Às vezes, você deseja usar nomes diferentes para diferentes associações. Se o nome que você deseja usar para uma associação no modelo não for o mesmo que a associação no :throughmodelo, você poderá usá :source-lo para especificá-lo.

Não acho que o parágrafo acima seja muito mais claro que o dos documentos, então aqui está um exemplo. Vamos supor que temos três modelos, Pet, Doge 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

Nesse caso, optamos por namespace the Dog::Breed, porque queremos acessar Dog.find(123).breedscomo uma associação agradável e conveniente.

Agora, se agora queremos criar uma has_many :dog_breeds, :through => :dogsassociação Pet, de repente temos um problema. O Rails não conseguirá encontrar uma :dog_breedsassociação Dog; portanto, o Rails não pode saber em qual Dog associação você deseja usar. Digite :source:

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

Com :source, estamos dizendo ao Rails para procurar uma associação chamada :breedsno Dogmodelo (como esse é o modelo usado :dogs) e usá-lo.

vonconrad
fonte
2
Eu acho que você quis dizer que a sua última classe Animal seria chamada classe Pet, apenas um erro de digitação, acredito.
27412 Kamilki81
3
No exemplo acima, a associação sob deve Dogser em has_many :breedvez de :breedse depois :sourceser :breedsingular, para representar o nome do modelo, em vez de :breedsqual representa o nome da tabela? Por exemplo has_many :dog_breeds, :through => :dogs, :source => :breed(sem ssufixo :breed)?
LazerSharks
1
Eu testei isso. é singular, sem ssufixo no #:source =>
Anwar
"Neste caso, escolhemos o namespace Dog :: Breed, porque queremos acessar Dog.find (123) .breeds como uma associação agradável e conveniente.". Você não precisa de um espaço para nome, não é?
precisa saber é o seguinte
201

Deixe-me expandir esse exemplo:

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

Com esse código, você pode fazer algo como Newsletter.find(id).usersobter uma lista dos assinantes do boletim. Mas se você quiser ser mais claro e conseguir digitar Newsletter.find(id).subscribers, deve alterar a classe Newsletter para isso:

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

Você está renomeando a usersassociação para subscribers. Se você não fornecer :source, o Rails procurará uma associação chamada subscriberna classe Subscription. Você precisa dizer para usar a userassociação na classe Subscription para fazer a lista de assinantes.

Jeremy Ruten
fonte
2
observe que os nomes dos modelos de singularização devem ser usados ​​em :source =>, e não no plural. Então, :usersestá errado,:user é correta
Anwar
Esta é a melhor resposta!
Brian Joseph Spinos
11

Resposta mais simples:

É o nome do relacionamento na tabela no meio.

Dreeub
fonte