Полиморфные отношения habtm с Rails / ActiveRecord

Как мне создать полиморфные отношения has_and_belongs_to_many с Rails / ActiveRecord?

Большинство примеров, которые я вижу, включают создание отношения own_to, которое ограничивает мою полиморфную сторону связью только с одним родителем:

Таблица: Задача

Таблица: Tasks_Targets

Таблица: CustomerStore

Таблица: SoftwareSystem

Тип CustomerStore и SoftwareSystem будет иметь тип & quot; Targetable & quot ;. в этом случае. Из того, что я понимаю, если я реализую полиморфные отношения, как показывает большинство примеров, я смогу только связать Targetable с задачейonce.

Некоторые уточнения могут помочь, так как большинство поисковых запросов в Интернете все еще оставляют некоторые объяснения этой теории необъяснимыми ...

Спасибо!

Ответы на вопрос(2)

Учитывая ваше объяснение вашего домена, я привел небольшой пример, основанный на тестировании того, как вы можете решить свою проблему. Если вы обнаружите какие-либо несоответствия в домене, пожалуйста, не стесняйтесь уточнить (я использую мойacts_as_fu gem на лету пробовать тестовые модели).

require 'acts_as_fu'

# class Task < ActiveRecord::Base
build_model(:tasks) do
  integer :task_target_id

  has_many :task_targets
  has_many :customer_stores, :through => :task_targets, :source => :targetable, :source_type => 'CustomerStore'
  has_many :software_systems, :through => :task_targets, :source => :targetable, :source_type => 'SoftwareSystem'
end

# class TaskTarget < ActiveRecord::Base
build_model(:task_targets) do
  string  :targetable_type
  integer :targetable_id
  integer :task_id

  belongs_to :targetable, :polymorphic => true
  belongs_to :task
end

# class CustomerStore < ActiveRecord::Base
build_model(:customer_stores) do
  has_many :task_targets, :as => :targetable
  has_many :tasks, :through => :task_targets
end

# class SoftwareSystem < ActiveRecord::Base
build_model(:software_systems) do
  has_many :task_targets, :as => :targetable
  has_many :tasks, :through => :task_targets
end

require 'test/unit'

class PolymorphicDomainTest < Test::Unit::TestCase
  # Test that customer stores can have multiple tasks
  def test_customer_store_gets_task
    task = Task.create!
    customer_store = CustomerStore.create!
    customer_store.task_targets.create! :task => task
    assert customer_store.tasks.include?(task)
  end

  def test_many_customer_stores_get_task
    task_a = Task.create!
    task_b = Task.create!
    customer_store = CustomerStore.create! :tasks => [task_a, task_b]
    assert customer_store.tasks.include?(task_a)
    assert customer_store.tasks.include?(task_b)
  end

  # Test that software systems can have multiple tasks
  def test_software_system_gets_task
    task = Task.create!
    software_system = SoftwareSystem.create!
    software_system.task_targets.create! :task => task
    assert software_system.tasks.include?(task)
  end

  def test_many_software_systems_get_task
    task_a = Task.create!
    task_b = Task.create!
    software_system = SoftwareSystem.create! :tasks => [task_a, task_b]
    assert software_system.tasks.include?(task_a)
    assert software_system.tasks.include?(task_b)
  end

  # Test that Tasks can have multiple customer stores
  def test_task_has_many_customer_stores
    task = Task.create!
    customer_store_a = CustomerStore.create!
    customer_store_b = CustomerStore.create!
    task.customer_stores = [customer_store_a, customer_store_b]
    task.save!
    task.reload
    assert task.customer_stores.include?(customer_store_a)
    assert task.customer_stores.include?(customer_store_b)
  end

  # Test that Tasks can have multiple software systems
  def test_task_has_many_software_systems
    task = Task.create!
    software_system_a = SoftwareSystem.create!
    software_system_b = SoftwareSystem.create!
    task.software_systems = [software_system_a, software_system_b]
    task.save!
    task.reload
    assert task.software_systems.include?(software_system_a)
    assert task.software_systems.include?(software_system_b)
  end
end
 Alexander Trauzzi14 июл. 2009 г., 23:24
Я грызу это в течение большей части сегодняшнего дня, и я все еще не совсем удовлетворен тем, что у меня есть ... Это связано с: "has_many: customer_stores,: through = & gt; : task_targets,: source = & gt; : targettable,: source_type = & gt; & APOS; CustomerStore & APOS; has_many: software_systems,: through = & gt; : task_targets,: source = & gt; : targettable,: source_type = & gt; & APOS; SoftwareSystem & APOS; & Quot; Что для меня побеждает цель полиморфных отношений все вместе. Есть ли способ настроить его так, чтобы я мог получить все таргетируемые объекты через Task.target AS WELL как способные к Targetable.tasks?
 Alexander Trauzzi14 июл. 2009 г., 15:25
Это выглядит довольно хорошо! Единственное, что меня беспокоит, это то, что я застрял в определении каждого "таргетинга" в разделе "Задачи". Есть ли способ обойти это? Как насчет Task.targets / targettables?

Чтобы дополнить ответ Накадзимы относительно вашей заботы, вот как я бы это сделал:

class Task < ActiveRecord::Base
  def targets
    # Get Array of all targetables
    tt = TaskTarget.select_all("SELECT targetable_type, targetable_id FROM task_targerts WHERE task_id = #{self[:id]}")

    # Build Hash of targetable_type => Array of targetable_ids
    targetables = Hash.new { |hash, key| hash[key] = [] }
    tt.each do |targetable|
      targetables[targetable.targetable_type] << targetable.targetable_id
    end

    # Query each "targetable" table once and merge all results
    targetables.keys.map{|key| (eval key).find(targetables[key])}.flatten
  end
end

Убедитесь, что индексировалиtask_id в таблицеtask_targets.

Ваш ответ на вопрос