Подтвердить что ты не робот

Has_many: через class_name и foreign_key

Я работаю с довольно простым has_many через: ситуация, когда я могу заставить параметры class_name/foreign_key работать в одном направлении, но не в другом. Возможно, вы можете мне помочь. (p.s. Я использую Rails 4, если это делает diff):

Русский: Пользователь управляет многими листингами через ЛистингManager, а листинг управляется многими пользователями через ЛистингManager. Диспетчер листинга имеет несколько полей данных, не связанных с этим вопросом, поэтому я отредактировал их в приведенном ниже коде

Здесь простую часть, которая работает:

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

поэтому единственным непростым здесь является то, что ListManager использует manager_id, а не user_id

В любом случае, вышеизложенное работает. Я могу позвонить user.listings, чтобы получить списки, связанные с пользователем, и я могу позвонить listing.managers, чтобы получить менеджеров, связанных с листингом.

Однако (и здесь вопрос), я решил, что было бы не очень важно сказать user.listings, так как пользователь может также "владеть", а не "управлять" списками, чего я действительно хотел: user.managed_listings, поэтому я исправил user.rb изменить   has_many: списки через:: listing_managers в   has_many: managed_listings через:: listing_managers, class_name: "Listing", foreign_key: "listing_id"

Это точная аналогия с кодом в listing.rb выше, поэтому я подумал, что это должно сработать сразу. Вместо этого мой rspec тест этого barfs, говоря 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. Не могли бы вы объяснить?

UPDATE: Я обновил код, чтобы отразить предложения @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 для доступа к его листингам?

4b9b3361

Ответ 1

Проблема здесь в том, что в

has_many :managers, through: :listing_managers

ActiveRecord может сделать вывод, что имя ассоциации в модели соединения (: listing_managers), поскольку оно имеет то же имя, что и ассоциация has_many :through, которую вы определяете. То есть, в обоих списках и в листингах-листингах есть много менеджеров.

Но это не так в вашей другой ассоциации. Там, list_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_name на другом has_many :through. Вы должны использовать их для определения прямых ассоциаций, а затем все, что вам нужно на has_many :through, - это точка к правильной ассоциации в модели :through.)

Ответ 2

Я знаю, что это старый вопрос, но я просто потратил некоторое время на те же ошибки и, наконец, понял это. Это то, что я сделал:

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

Это таблица соединений joinManager выглядит так:

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

Надеюсь, это поможет будущим искателям.

Ответ 3

у меня было несколько проблем с моими моделями, мне пришлось добавить внешний ключ, а также источник и имя класса... это был единственный обходной путь, который я нашел:

  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'