多态habtm与Rails/ActiveRecord的关系

问题描述:

我将如何去创建一个多态has_and_belongs_to_many与Rails/ActiveRecord的关系?多态habtm与Rails/ActiveRecord的关系

多数时候我看到涉及创建这限制了我的多态,一边被相关只有一个父一个belongs_to的关系的实例:

表:任务

表:Tasks_Targets

表:CustomerStore

表:SoftwareSystem

CustomerStore和SoftwareSystem都是ty pe在这种情况下是“目标”。根据我的理解,如果我通过大多数示例显示实现多态关系,我只能将目标与任务关联一次

一些澄清可能有助于大多数在线搜索仍然留下一些背后的关系不明原因的理论......

谢谢!

鉴于你对域名的解释,我提出了一个小测试驱动的例子,说明如何解决你的问题。如果您发现任何域名不一致的情况,请随时进一步澄清(我正在使用我的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 
+0

这看起来不错!我唯一担心的是我坚持在“任务”中定义每个“可定位”。有没有办法解决这个问题? Task.targets/targetables呢? – 2009-07-14 13:25:49

为了配合中岛与问候回答您的问题,这是我会怎么做:

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_idtask_targets