ruby-on-rails - tutorial - ruby on rails update controller
has_many: a través de class_name y foreign_key (2)
El problema aquí es que en
has_many :managers, through: :listing_managers
ActiveRecord puede inferir que el nombre de la asociación en el modelo join (: listing_managers) porque tiene el mismo nombre que has_many :through
asociación que está definiendo. Es decir, tanto los listados como los list_mangers tienen muchos administradores .
Pero ese no es el caso en su otra asociación. Allí, un listing_manager tiene muchas has_many :listings
, pero un usuario has_many :managed_listings
. Entonces ActiveRecord no puede inferir el nombre de la asociación en ListingManager
que debería usar.
Para esto es la opción :source
(ver http://guides.rubyonrails.org/association_basics.html#has-many-association-reference ). Entonces la declaración correcta sería:
has_many :managed_listings, through: :listing_managers, source: :listing
(ps no necesitas las :foreign_key
o :class_name
en la otra has_many :through
. Las :class_name
para definir asociaciones directas , y luego todo lo que necesitas en una has_many :through
es apuntar a la asociación correcta en el :through
modelo.)
Estoy trabajando con una situación bastante sencilla de has_many through: donde puedo hacer que los parámetros class_name / foreign_key funcionen en una dirección pero no en la otra. Quizás puedas ayudarme. (ps estoy usando Rails 4 si eso hace una diferencia):
Inglés: un usuario gestiona muchos listados a través de ListingManager, y un listado es administrado por muchos usuarios a través de ListingManager. El administrador de listas tiene algunos campos de datos, que no guardan relación con esta pregunta, así que los edité en el siguiente código
Aquí está la parte simple que funciona:
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
Como puede adivinar desde arriba, la tabla de ListingManager se ve así:
create_table "listing_managers", force: true do |t|
t.integer "listing_id"
t.integer "manager_id"
end
así que el único no simple aquí es que ListingManager usa manager_id
lugar de user_id
De todos modos, lo anterior funciona. Puedo llamar a user.listings
para obtener los Listings asociados con el usuario, y puedo llamar a listing.managers
para obtener los administradores asociados con el listado.
Sin embargo (y esta es la pregunta), decidí que no era demasiado significativo decir user.listings
ya que un usuario también puede "poseer" en lugar de "administrar" listas, lo que realmente quería era user.managed_listings
así que puse user.rb
para cambiar has_many: listados, a través de:: listing_managers a has_many: managed_listings, a través de:: listing_managers, class_name: "Listing", foreign_key: "listing_id"
Esta es una analogía exacta del código en listing.rb
arriba, así que pensé que esto debería funcionar de inmediato. En cambio, mi prueba rspec de este barfs diciendo 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?
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?
la prueba es:
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
Ahora, estoy convencido de que no sé nada. Sí, creo que podría hacer un alias, pero me molesta que la misma técnica utilizada en listing.rb
no parezca funcionar en user.rb
¿Puedes ayudar a explicar?
ACTUALIZACIÓN: actualicé el código para reflejar las sugerencias de @gregates, pero todavía me encuentro con un problema: escribí una prueba adicional que falla (y se confirma por "mano": prueba en la consola de Rails). Cuando uno escribe una prueba como esta:
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
Lo anterior falla Rails genera el error PG::UndefinedColumn: ERROR: column listing_managers.user_id does not exist
(debería estar buscando ''listing_managers.manager_id''). Así que creo que todavía hay un error en el lado del usuario de la asociación. En user.rb
''s has_many :managed_listings, through: :listing_managers, source: :listing
, ¿cómo sabe el usuario usar manager_id
para acceder a sus listados?
Sé que esta es una vieja pregunta, pero acabo de pasar un tiempo corriendo en los mismos errores y finalmente lo descubrí. Esto es lo que hice:
class User < ActiveRecord::Base
has_many :listing_managers
has_many :managed_listings, through: :listing_managers, source: :listing
end
class Listing < ActiveRecord::Base
has_many :listing_managers
has_many :managers, through: :listing_managers, source: :user
end
class ListingManager < ActiveRecord::Base
belongs_to :listing
belongs_to :user
end
A esto se parece la tabla Join de ListingManager:
create_table :listing_managers do |t|
t.integer :listing_id
t.integer :user_id
end
Espero que esto ayude a los buscadores futuros.