简体   繁体   English

has_many:通过class_name和foreign_key

[英]has_many :through with class_name and foreign_key

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. 我正在使用相当简单的has_many:我可以使class_name / foreign_key参数在一个方向上工作但在另一个方向上工作的情况。 Perhaps you can help me out. 也许你可以帮助我。 (ps I'm using Rails 4 if that makes a diff): (ps我正在使用Rails 4,如果这会产生差异):

English: A User manages many Listings through ListingManager, and a Listing is managed by many Users through ListingManager. 英语:用户通过ListingManager管理许多列表,并且许多用户通过ListingManager管理列表。 Listing manager has some data fields, not germane to this question, so I edited them out in the below code 清单管理器有一些数据字段,与这个问题没有密切关系,因此我在下面的代码中对它们进行了编辑

Here's the simple part which works: 这是有效的简单部分:

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

as you can guess from above the ListingManager table looks like: 正如您可以从上面猜测的那样,ListManager表看起来像:

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

so the only non-simple here is that ListingManager uses manager_id rather than user_id 所以这里唯一不简单的是ListingManager使用manager_id而不是user_id

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来获取与用户关联的列表,我可以调用listing.managers来获取与列表相关联的管理器。

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" 然而(这里是问题),我认为说user.listings并不是非常有意义,因为用户也可以“拥有”而不是“管理”列表,我真正想要的是user.managed_listings所以我调整了user.rb更改has_many:listing,通过:: listing_managers到has_many:managed_listings,通过:: listing_managers,class_name:“Listing”,foreign_key:“listing_id”

This is an exact analogy to the code in listing.rb above, so I thought this should work right off. 这与上面的listing.rb的代码完全类似,所以我认为这应该可以正常工作。 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? 相反,我通过说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?对这个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? 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?

the test being: 测试是:

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

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 . 是的,我想我可以做一个别名,但我很担心listing.rb中使用的相同技术似乎在user.rb不起作用。 Can you help explain? 你能解释一下吗?

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). 更新:我更新了代码以反映@gregates的建议,但我仍然遇到了一个问题:我写了一个额外的测试失败(并在Rails控制台中通过“手”确认)。 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

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'). Rails生成错误PG::UndefinedColumn: ERROR: column listing_managers.user_id does not exist (它应该查找'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) ? user.rbhas_many :managed_listings, through: :listing_managers, source: :listing ,用户如何知道使用manager_id来获取它的列表?

The issue here is that in 这里的问题是

has_many :managers, through: :listing_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. ActiveRecord可以推断出连接模型上的关联名称(:listing_managers), 因为它与您定义的has_many :through关联具有相同的名称 That is, both listings and listing_mangers have many managers . 也就是说,listing和listing_mangers都有很多经理

But that's not the case in your other association. 但是你的其他协会并非如此。 There, a listing_manager has_many :listings , but a user has_many :managed_listings . 在那里,listing_manager has_many :listings listing,但是用户has_many :managed_listings So ActiveRecord is unable to infer the name of the association on ListingManager that it should use. 因此,ActiveRecord无法推断它应该使用的ListingManager上的关联名称。

This is what the :source option is for (see http://guides.rubyonrails.org/association_basics.html#has-many-association-reference ). 这就是:source选项的用途(参见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 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.) (ps你实际上并不需要:foreign_key:class_name选项另一个has_many :through 。你可以用它们来定义直接关联,然后你在has_many :through上所需要的只是has_many :through是指向正确的关联:through模型。)

I know this is an old question, but I just spent some time running into the same errors and finally figured it out. 我知道这是一个老问题,但我只是花了一些时间遇到同样的错误,最后想出来了。 This is what I did: 这就是我做的:

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

This is what the ListingManager join table looks like: 这就是ListingManager连接表的样子:

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

Hope this helps future searchers. 希望这有助于未来的搜索者。

i had several issues with my models, had to add the foreign key as well as the source and class name... this was the only workaround i found: 我的模型有几个问题,必须添加外键以及源和类名...这是我发现的唯一解决方法:

  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'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM