Compréhension: option source de has_one / has_many via Rails

184

Veuillez m'aider à comprendre l' :sourceoption d' has_one/has_many :throughassociation. L'explication de l'API Rails n'a que peu de sens pour moi.

« Indique le nom de l' association source utilisée par has_many :through => :queries. Utilisez uniquement si le nom ne peut être déduit de l'association. has_many :subscribers, :through => :subscriptionsCherchera soit :subscribersou :subscribersur Subscription, à moins qu'une :sourceest donnée. »

Tri Vuong
la source

Réponses:

238

Parfois, vous souhaitez utiliser des noms différents pour différentes associations. Si le nom que vous souhaitez utiliser pour une association sur le modèle n'est pas le même que l'assocation sur le :throughmodèle, vous pouvez l'utiliser :sourcepour le spécifier.

Je ne pense pas que le paragraphe ci-dessus soit beaucoup plus clair que celui de la documentation, voici donc un exemple. Supposons que nous ayons trois modèles Pet, Doget 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

Dans ce cas, nous avons choisi d'espacer le nom Dog::Breed, car nous voulons y accéder Dog.find(123).breedscomme une association agréable et pratique.

Maintenant, si nous voulons maintenant créer une has_many :dog_breeds, :through => :dogsassociation sur Pet, nous avons soudainement un problème. Rails ne pourra pas trouver d' :dog_breedsassociation sur Dog, donc Rails ne peut pas savoir quelle Dog association vous souhaitez utiliser. Entrez :source:

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

Avec :source, nous demandons à Rails de rechercher une association appelée :breedssur le Dogmodèle (car c'est le modèle utilisé pour :dogs), et de l'utiliser.

Vonconrad
la source
2
Je pense que vous vouliez que votre dernier animal de classe s'appelle Pet de classe, juste une faute de frappe je crois.
Kamilski81 le
3
Dans l'exemple ci - dessus, si la l'association en Dogêtre au has_many :breedlieu de :breedspuis l' :sourceêtre :breedsingulier, pour représenter le nom du modèle, au lieu de ce :breedsqui représente le nom de la table? Par exemple has_many :dog_breeds, :through => :dogs, :source => :breed(pas de ssuffixe :breed)?
LazerSharks
1
J'ai testé ça. il est singulier, pas de ssuffixe dans le:source =>
Anwar
«Dans ce cas, nous avons choisi de nommer Dog :: Breed, car nous voulons accéder à Dog.find (123) .breeds comme une association agréable et pratique.». Vous n'avez pas besoin d'un espace de noms pour cela, n'est-ce pas?
Jwan622
201

Permettez-moi de développer cet exemple:

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

Avec ce code, vous pouvez faire quelque chose comme Newsletter.find(id).usersobtenir une liste des abonnés à la newsletter. Mais si vous voulez être plus clair et pouvoir taper à la Newsletter.find(id).subscribersplace, vous devez changer la classe Newsletter en ceci:

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

Vous renommez l' usersassociation en subscribers. Si vous ne fournissez pas le :source, Rails recherchera une association appelée subscriberdans la classe Subscription. Vous devez lui dire d'utiliser l' userassociation dans la classe Subscription pour faire la liste des abonnés.

Jeremy Ruten
la source
2
notez que les noms de modèle singularize doivent être utilisés au :source =>pluriel et non au pluriel. Donc, :usersest faux, :userest correct
Anwar
C'est la meilleure réponse!, Permettez-moi de souligner cette partie: "Vous renommez l'association d'utilisateurs en abonnés. Si vous ne fournissez pas le: source, Rails recherchera une association appelée abonné dans la classe d'abonnement."
Brian Joseph Spinos
11

Réponse la plus simple:

Est le nom de la relation dans la table au milieu.

Dreeub
la source