Sto lavorando con has_many abbastanza semplice: situazione in cui posso fare in modo che i parametri class_name/foreign_key funzionino in una direzione ma non nell'altra. Forse puoi aiutarmi. (p.s.so sto usando Rails 4 se questo fa un diff):has_many: through con class_name e foreign_key
inglese: un utente gestisce molti elenchi tramite ListingManager, e un elenco è gestito da molti utenti tramite ListingManager. direttore Listing ha alcuni campi di dati, non germane a questa domanda, così li ho modificato nel codice qui sotto
Qui è la parte semplice che funziona:
class User < ActiveRecord::Base
has_many :listing_managers
has_many :listings, through: :listing_managers
end
class Listing < ActiveRecord::Base
has_many :listing_managers
has_many :managers, through: :listing_managers, class_name: "User", foreign_key: "manager_id"
end
class ListingManager < ActiveRecord::Base
belongs_to :listing
belongs_to :manager, class_name:"User"
attr_accessible :listing_id, :manager_id
end
come si può intuire da sopra gli sguardi tabella ListingManager come:
create_table "listing_managers", force: true do |t|
t.integer "listing_id"
t.integer "manager_id"
end
quindi l'unica non semplice ecco che utilizza ListingManager manager_id
anziché user_id
Ad ogni modo, quanto sopra funziona. Posso chiamare lo user.listings
per ottenere gli elenchi associati all'utente e posso chiamare lo listing.managers
per ottenere i gestori associati all'elenco.
Tuttavia (e qui è la domanda), ho deciso che non era terribilmente significativo da dire user.listings
dal momento che un utente può anche "proprio", piuttosto che "gestire" in lista, quello che volevo era user.managed_listings
così ho ottimizzato user.rb
cambiare has_many: annunci, attraverso:: listing_managers a has_many: managed_listings, attraverso:: listing_managers, nome_classe: "Annuncio", foreign_key: "listing_id"
Questa è un'analogia esatta al codice in listing.rb
sopra, così ho pensato che questo dovrebbe funzionare subito. Invece il mio test RSpec di questa barfs dicendo essere ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :managed_listing or :managed_listings in model ListingManager. Try 'has_many :managed_listings, :through => :listing_managers, :source => <name>'. Is it one of :listing or :manager?
il test:
it "manages many managed_listings" do
user = FactoryGirl.build(:user)
l1 = FactoryGirl.build(:listing)
l2 = FactoryGirl.build(:listing)
user.managed_listings << l1
user.managed_listings << l2
expect(@user.managed_listings.size).to eq 2
end
Ora, io sono convinto io non so nulla. Sì, immagino di poter fare un alias, ma mi preoccupo che la stessa tecnica utilizzata in listing.rb
non funzioni nel user.rb
. Puoi aiutarmi a spiegare?
AGGIORNAMENTO: Ho aggiornato il codice per riflettere i suggerimenti di @gregates, ma sto ancora incontrando un problema: ho scritto un test aggiuntivo che fallisce (e confermato da "hand" -tesing nella console di Rails). Quando si scrive un test come questo:
it "manages many managed_listings" do
l1 = FactoryGirl.create(:listing)
@user = User.last
ListingManager.destroy_all
@before_count = ListingManager.count
expect( @before_count).to eq 0
lm = FactoryGirl.create(:listing_manager, manager_id: @user.id, listing_id: l1.id)
expect(@user.managed_listings.count).to eq 1
end
Quanto sopra non riesce. Rails genera l'errore PG::UndefinedColumn: ERROR: column listing_managers.user_id does not exist
(Dovrebbe cercare 'listing_managers.manager_id'). Quindi penso che ci sia ancora un errore sul lato utente dell'associazione. In user.rb
's has_many :managed_listings, through: :listing_managers, source: :listing
, come fa l'Utente a sapere di usare manager_id
per ottenere il suo elenco (s)?
interessante ... e ho provato 'source:" Listing "' ma non ha funzionato. Quindi il simbolo è l'unico modo per impostarlo. – user2669055
Segnalo come risposta (e grazie per il veloce!) Anche se sono ancora un po 'confuso sul problema della fonte: - Ho trascorso molto tempo con la Guida che hai citato, ma la documentazione per la fonte è molto MOLTO leggera. – user2669055
Il punto chiave credo sia in p.s. Ho aggiunto. Per un'associazione * attraverso * un modello di join, devi solo dirgli il nome dell'associazione a cui stai partecipando (se non può essere dedotto). Il resto delle opzioni non ha importanza. Quelli sono per le associazioni * dirette *. E questo è tutto ciò che fa 'source', è specificare l'associazione da utilizzare sul modello di join per un'associazione 'through'. – gregates