has_many:通过class_name和foreign_key

时间:2013-08-09 20:01:45

标签: ruby-on-rails ruby-on-rails-4

我正在使用相当简单的has_many:我可以使class_name / foreign_key参数在一个方向上工作但在另一个方向上工作的情况。也许你可以帮助我。 (p.s.我正在使用Rails 4,如果它产生差异):

英语:用户通过ListingManager管理许多列表,并且许多用户通过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

正如您可以从上面猜到的那样,ListManager表看起来像:

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并不是非常有意义,因为用户也可以“拥有”而不是“管理”列表,我真正想要的是{{1}所以我调整了user.managed_listings来改变     has_many:listing,through :: listing_managers 至     has_many:managed_listings,通过:: listing_managers,class_name:“listing”,foreign_key:“listing_id”

这与上面user.rb中的代码完全类似,所以我认为这应该可以正常工作。相反,我对这个barfs的rspec测试说 listing.rb

测试是:

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。你能解释一下吗?

更新: 我更新了代码以反映@gregates的建议,但我仍然遇到了一个问题:我写了一个额外的测试失败(并在Rails控制台中通过“手”确认)。当一个人写这样的测试时:

user.rb

以上失败。 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 (它应该寻找'listing_managers.manager_id')。所以我认为协会的用户方面仍然存在错误。在PG::UndefinedColumn: ERROR: column listing_managers.user_id does not exist的{​​{1}}中,用户如何知道使用user.rb来访问其列表?

3 个答案:

答案 0 :(得分:46)

这里的问题是

has_many :managers, through: :listing_managers

ActiveRecord可以推断出连接模型(:listing_managers)上的关联名称,因为它与您定义的has_many :through关联具有相同的名称。也就是说,listing和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

(ps你实际上不需要另一个:foreign_key上的:class_namehas_many :through选项。你可以使用它们来定义直接关联,然后has_many :through上所需的只是指向:through模型上的正确关联。)

答案 1 :(得分:9)

我知道这是一个老问题,但我只是花了一些时间遇到同样的错误,最后想出来了。这就是我所做的:

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

希望这有助于未来的搜索者。

答案 2 :(得分:0)

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

instance