has_many :通过 class_name 和 foreign_key [英] has_many :through with class_name and foreign_key

查看:30
本文介绍了has_many :通过 class_name 和 foreign_key的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理一个相当简单的 has_many through: 情况,在这种情况下,我可以使 class_name/foreign_key 参数在一个方向上起作用而不是另一个方向.或许你可以帮帮我.(ps.如果有差异,我将使用 Rails 4):

I'm working with a fairly straightforward has_many through: situation where I can make the class_name/foreign_key parameters work in one direction but not the other. Perhaps you can help me out. (p.s. I'm using Rails 4 if that makes a diff):

英文:一个User通过ListingManager管理多个Listing,一个Listing通过ListingManager被多个User管理.Listing manager 有一些数据字段,与这个问题无关,所以我在下面的代码中编辑了它们

English: A User manages many Listings through ListingManager, and a Listing is managed by many Users through ListingManager. Listing manager has some data fields, not germane to this question, so I edited them out in the below code

这是最简单的部分:

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 表看起来像:

as you can guess from above the ListingManager table looks like:

create_table "listing_managers", force: true do |t|
  t.integer  "listing_id"
  t.integer  "manager_id"
end

所以这里唯一不简单的是 ListingManager 使用 manager_id 而不是 user_id

so the only non-simple here is that ListingManager uses manager_id rather than user_id

无论如何,上述工作.我可以调用 user.listings 来获取与用户关联的列表,我可以调用 listing.managers 来获取与列表关联的经理.

Anyway, the above works. I can call user.listings to get the Listings associated with the user, and I can call listing.managers to get the managers associated with the listing.

然而(这是问题),我认为说 user.listings 并没有太大意义,因为用户也可以拥有"而不是管理"列表,这是我真正想要的是 user.managed_listings 所以我调整了 user.rb 来改变has_many :listings, 通过: :listing_managers至has_many :managed_listings, through: :listing_managers, class_name: "Listing", foreign_key: "listing_id"

However (and here's the question), I decided it wasn't terribly meaningful to say user.listings since a user can also "own" rather than "manage" listings, what I really wanted was user.managed_listings so I tweaked user.rb to change has_many :listings, through: :listing_managers to has_many :managed_listings, through: :listing_managers, class_name: "Listing", foreign_key: "listing_id"

这与上面 listing.rb 中的代码完全相似,所以我认为这应该可以正常工作.相反,我说这个 barfs 的 rspec 测试ActiveRecord::HasManyThroughSourceAssociationNotFoundError:在模型 ListingManager 中找不到源关联 :managed_listing 或 :managed_listings.尝试 'has_many :managed_listings, :through =>:listing_managers, :source =><名称>'.它是 :listing 还是 :manager 之一?

This is an exact analogy to the code in listing.rb above, so I thought this should work right off. Instead my rspec test of this barfs by saying 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 中似乎不起作用.你能帮忙解释一下吗?

Now, I'm convinced I know nothing. Yes, I guess I could do an alias, but I'm bothered that the same technique used in listing.rb doesn't seem to work in user.rb. Can you help explain?

更新:我更新了代码以反映@gregates 的建议,但我仍然遇到了一个问题:我编写了一个失败的附加测试(并通过 Rails 控制台中的手动"测试确认).当人们编写这样的测试时:

UPDATE: I updated the code to reflect @gregates suggestions, but I'm still running into a problem: I wrote an additional test which fails (and confirmed by "hand"-tesing in the Rails console). When one writes a test like this:

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.rbhas_many :managed_listings中,通过::listing_managers, source: :listing,User如何知道使用manager_id得到到其列表?

The above fails. Rails generates the error PG::UndefinedColumn: ERROR: column listing_managers.user_id does not exist (It should be looking for 'listing_managers.manager_id'). So I think there's still an error on the User side of the association. In user.rb's has_many :managed_listings, through: :listing_managers, source: :listing, how does User know to use manager_id to get to its Listing(s) ?

推荐答案

这里的问题是在

has_many :managers, through: :listing_managers

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

ActiveRecord can infer that the name of the association on the join model (:listing_managers) because it has the same name as the has_many :through association you're defining. That is, both listings and listing_mangers have many managers.

但在您的其他协会中并非如此.在那里,一个 listing_manager has_many :listings,但一个用户 has_many :managed_listings.因此 ActiveRecord 无法推断出它应该使用的 ListingManager 上的关联名称.

But that's not the case in your other association. There, a listing_manager has_many :listings, but a user has_many :managed_listings. So ActiveRecord is unable to infer the name of the association on ListingManager that it should use.

这就是 :source 选项的用途(参见 http://guides.rubyonrails.org/association_basics.html#has-many-association-reference).所以正确的声明是:

This is what the :source option is for (see http://guides.rubyonrails.org/association_basics.html#has-many-association-reference). So the correct declaration would be:

has_many :managed_listings, through: :listing_managers, source: :listing

(ps 您实际上并不需要其他 has_many :through 上的 :foreign_key:class_name 选项.您会使用那些定义直接关联,然后在 has_many :through 上你需要的就是指向 :through 模型上的正确关联.)

(p.s. you don't actually need the :foreign_key or :class_name options on the other has_many :through. You'd use those to define direct associations, and then all you need on a has_many :through is to point to the correct association on the :through model.)

这篇关于has_many :通过 class_name 和 foreign_key的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆