Comprensión: opción de fuente de has_one / has_many a través de Rails

184

Por favor, ayúdame a entender la :sourceopción de has_one/has_many :throughasociación. La explicación de Rails API tiene muy poco sentido para mí.

"Especifica el nombre de la asociación fuente utilizada por has_many :through => :queries. Sólo se debe usar si el nombre no se puede deducir de la asociación. has_many :subscribers, :through => :subscriptionsBuscará cualquiera :subscriberso :subscriberen Subscription, a menos que :sourcese le da."

Tri Vuong
fuente

Respuestas:

238

A veces, desea utilizar diferentes nombres para diferentes asociaciones. Si el nombre que desea usar para una asociación en el modelo no es el mismo que la asociación en el :throughmodelo, puede usarlo :sourcepara especificarlo.

No creo que el párrafo anterior sea mucho más claro que el de los documentos, así que aquí hay un ejemplo. Supongamos que tenemos tres modelos Pet, Dogy 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

En este caso, hemos elegido el espacio de nombres Dog::Breed, porque queremos acceder Dog.find(123).breedscomo una asociación agradable y conveniente.

Ahora, si ahora queremos crear una has_many :dog_breeds, :through => :dogsasociación Pet, de repente tenemos un problema. Rails no podrá encontrar una :dog_breedsasociación Dog, por lo que Rails no puede saber qué Dog asociación desea utilizar. Ingrese :source:

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

Con :source, le estamos diciendo a Rails que busque una asociación llamada :breedsen el Dogmodelo (ya que ese es el modelo utilizado :dogs), y que la use.

vonconrad
fuente
2
Creo que querías decir que tu última clase Animal se llamaría clase Mascota, solo un error tipográfico, creo.
Kamilski81
3
En el ejemplo anterior, ¿debería la asociación bajo Dogser en has_many :breedlugar de :breedsy luego :sourceser :breedsingular, para representar el nombre del modelo, en lugar de :breedscuál representa el nombre de la tabla? Por ejemplo has_many :dog_breeds, :through => :dogs, :source => :breed(sin ssufijo :breed)?
LazerSharks
1
He probado esto. es singular, sin ssufijo en el:source =>
Anwar
"En este caso, hemos elegido el espacio de nombres Dog :: Breed, porque queremos acceder a Dog.find (123) .breeds como una asociación agradable y conveniente". No necesitas un espacio de nombres para eso, ¿verdad?
Jwan622
201

Permítanme ampliar ese ejemplo:

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

Con este código, puede hacer algo como Newsletter.find(id).usersobtener una lista de los suscriptores del boletín. Pero si desea ser más claro y poder escribir en su Newsletter.find(id).subscriberslugar, debe cambiar la clase Newsletter a esto:

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

Estás cambiando el nombre de la usersasociación a subscribers. Si no proporciona el :source, Rails buscará una asociación llamada subscriberen la clase de Suscripción. Debe indicarle que use la userasociación en la clase Suscripción para hacer la lista de suscriptores.

Jeremy Ruten
fuente
2
tenga en cuenta que los nombres de modelo en singular deben usarse en :source =>, no en plural. Entonces, :usersestá mal, :useres correcto
Anwar
¡Esta es la mejor respuesta !, permítanme enfatizar esta parte: "Está cambiando el nombre de la asociación de usuarios a suscriptores. Si no proporciona la fuente: Rails buscará una asociación llamada suscriptor en la clase de Suscripción".
Brian Joseph Spinos
11

Respuesta más simple:

Es el nombre de la relación en la tabla del medio.

Dreeub
fuente