Zrozumienie: opcja źródła has_one / has_many through Rails

184

Proszę o pomoc w zrozumieniu :sourceopcji has_one/has_many :throughstowarzyszenia. Wyjaśnienie interfejsu Rails API nie ma dla mnie większego sensu.

„Określa nazwę powiązania źródłowego używanego przez has_many :through => :queries. Użyj go tylko wtedy, gdy nazwy nie można wywnioskować z powiązania.has_many :subscribers, :through => :subscriptions Będzie szukał jednego :subscriberslub :subscriberdrugiego Subscription, chyba że :sourcepodano a.”

Tri Vuong
źródło

Odpowiedzi:

238

Czasami chcesz użyć różnych nazw dla różnych skojarzeń. Jeśli nazwa, której chcesz użyć dla powiązania w modelu, nie jest taka sama jak przypisanie w :throughmodelu, możesz jej użyć :source.

Nie sądzę, aby powyższy akapit był o wiele jaśniejszy niż ten w dokumentacji, więc oto przykład. Załóżmy, że mamy trzy modele Pet, Dogi 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

W tym przypadku wybraliśmy przestrzeń nazw Dog::Breed, ponieważ chcemy uzyskać dostęp Dog.find(123).breedsjako miłe i wygodne skojarzenie.

Teraz, jeśli chcemy teraz utworzyć has_many :dog_breeds, :through => :dogspowiązanie Pet, nagle mamy problem. Railsy nie będą w stanie znaleźć :dog_breedspowiązania Dog, więc Railsy nie mogą wiedzieć, z którego Dog skojarzenia chcesz skorzystać. Wpisz :source:

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

Z :source, mówimy szyn szukać stowarzyszenie o nazwie :breedsna Dogmodelu (jak to model używany do :dogs), a stosowanie tego.

vonconrad
źródło
2
Myślę, że chciałeś, aby twoja ostatnia klasa Animal nazywała się class Pet, tylko literówka, którą wierzę.
Kamilski81,
3
W powyższym przykładzie, czy stowarzyszenie powinno Dogbyć has_many :breedzamiast, :breedsa następnie :sourcebyć :breedpojedynczym, reprezentujące nazwę modelu, zamiast :breedsktórego reprezentuje nazwę tabeli? Np. has_many :dog_breeds, :through => :dogs, :source => :breed(Bez sprzyrostka :breed)?
LazerSharks
1
Przetestowałem to. jest to liczba pojedyncza, brak sprzyrostka w:source =>
Anwar
„W tym przypadku wybraliśmy przestrzeń nazw Dog :: Breed, ponieważ chcemy uzyskać dostęp do Dog.find (123) .breed jako miłego i wygodnego skojarzenia.” Nie potrzebujesz do tego przestrzeni nazw?
Jwan622,
201

Pozwól mi rozwinąć ten przykład:

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

Za pomocą tego kodu możesz zrobić coś takiego, jak Newsletter.find(id).usersuzyskać listę subskrybentów newslettera. Ale jeśli chcesz być bardziej zrozumiały i Newsletter.find(id).subscribersmożesz zamiast tego pisać , musisz zmienić klasę Newsletter na:

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

Zmieniasz nazwę userspowiązania na subscribers. Jeśli nie podasz :source, Rails będzie szukał powiązania wywoływanego subscriberw klasie subskrypcji. Musisz powiedzieć mu, aby użył userpowiązania w klasie subskrypcji, aby utworzyć listę subskrybentów.

Jeremy Ruten
źródło
2
należy zauważyć, że należy używać nazw modeli w liczbie pojedynczej :source =>, a nie w liczbie mnogiej. Więc :usersjest źle, :userjest poprawne
Anwar
To najlepsza odpowiedź! Pozwólcie, że po prostu podkreślę tę część: „Zmieniasz nazwę powiązania użytkowników z subskrybentami. Jeśli nie podasz: źródła, Rails będzie szukał powiązania zwanego subskrybentem w klasie subskrypcji”.
Brian Joseph Spinos
11

Najprostsza odpowiedź:

Jest nazwa relacji w tabeli na środku.

Dreeub
źródło