46

我正在处理一个相当简单的 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,通过: : list_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. 你能帮忙解释一下吗?

更新:我更新了代码以反映@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'shas_many :managed_listings, through: :listing_managers, source: :listing中,用户如何知道使用manager_id它来访问其列表?

4

3 回答 3

55

这里的问题是

has_many :managers, through: :listing_managers

ActiveRecord 可以推断出连接模型 (:listing_managers) 上的关联名称,因为它has_many :through与您定义的关联名称相同。也就是说, listings 和 listing_mangers 都有很多manager

但在您的其他协会中并非如此。在那里,一个 listing_manager has_many :listings,但是一个 user 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

(ps您实际上并不需要:foreign_keyor:class_name选项has_many :through。您将使用它们来定义直接关联,然后您需要在 ahas_many :through上指向:through模型上的正确关联。)

于 2013-08-09T20:17:59.483 回答
14

我知道这是一个老问题,但我只是花了一些时间遇到同样的错误,终于弄明白了。这就是我所做的:

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

这是 ListingManager 连接表的样子:

create_table :listing_managers do |t|
  t.integer :listing_id
  t.integer :user_id
end

希望这对未来的搜索者有所帮助。

于 2017-01-12T22:19:30.433 回答
1

我的模型有几个问题,必须添加外键以及源和类名......这是我找到的唯一解决方法:

  has_many :ticket_purchase_details, foreign_key: :ticket_purchase_id, source: :ticket_purchase_details, class_name: 'TicketPurchaseDetail'

  has_many :details, through: :ticket_purchase_details, source: :ticket_purchase, class_name: 'TicketPurchaseDetail'
于 2019-08-07T04:51:19.450 回答