我正在处理一个相当简单的 has_many through: 情况,我可以使 class_name/foreign_key 参数在一个方向上工作,但不能在另一个方向上工作。也许你能帮帮我。 (附注:我使用的是 Rails 4,如果这会产生差异):
英文:一个User通过ListingManager管理多个Listing,一个Listing由多个User通过ListingManager管理。列表管理器有一些数据字段,与这个问题无关,所以我在下面的代码中编辑了它们
这是有效的简单部分:
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
正如您可以从上面的 ListingManager 表中猜到的那样:
create_table "listing_managers", force: true do |t|
t.integer "listing_id"
t.integer "manager_id"
end
所以这里唯一不简单的是ListingManager使用
manager_id
而不是 user_id
无论如何,上述工作。我可以调用
user.listings
获取与用户关联的列表,我可以调用 listing.managers
获取与列表关联的经理。但是(这是问题所在),我认为说
user.listings
并没有多大意义。因为用户也可以“拥有”而不是“管理”列表,所以我真正想要的是 user.managed_listings
所以我调整了user.rb
改变has_many :listings, 通过: :listing_managers
到
has_many:managed_listings,通过::listing_managers,class_name:“Listing”,foreign_key:“listing_id”
这与
listing.rb
中的代码完全类似。上面,所以我认为这应该可以正常工作。相反,我对这个 barfs 的 rspec 测试是这样说的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?
测试是:
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
现在,我确信我什么都不知道。是的,我想我可以做一个别名,但我很困扰
listing.rb
中使用的相同技术似乎在 user.rb
中不起作用.你能帮忙解释一下吗?更新:
我更新了代码以反射(reflect)@gregates 的建议,但我仍然遇到了一个问题:我编写了一个失败的附加测试(并通过 Rails 控制台中的“手动”测试确认)。当一个人编写这样的测试时:
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
以上失败。 Rails 生成错误
PG::UndefinedColumn: ERROR: column listing_managers.user_id does not exist
(它应该在寻找“listing_managers.manager_id”)。所以我认为关联的用户端仍然存在错误。在 user.rb
的has_many :managed_listings, through: :listing_managers, source: :listing
,用户怎么知道使用manager_id
去它的列表(S)?
最佳答案
这里的问题是
has_many :managers, through: :listing_managers
ActiveRecord 可以推断出连接模型上的关联名称 (:listing_managers),因为它与 has_many :through
具有相同的名称。您正在定义的关联。也就是说,listings 和 listing_mangers 都有很多管理器。但在您的其他协会中并非如此。在那里,一个listing_manager
has_many :listings
, 但用户 has_many :managed_listings
.因此 ActiveRecord 无法推断 ListingManager
上的关联名称它应该使用。这就是
:source
选项适用于(见 http://guides.rubyonrails.org/association_basics.html#has-many-association-reference)。所以正确的声明是:has_many :managed_listings, through: :listing_managers, source: :listing
(附注:您实际上并不需要其他 :foreign_key
上的 :class_name
或 has_many :through
选项。您可以使用这些选项来定义直接关联,然后在 has_many :through
上只需要指向正确的:through
模型上的关联。)
关于ruby-on-rails - has_many :through with class_name and foreign_key,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18154736/