0

我有一个名为 Address 的多态模型。我想要实现的是将许多地址绑定到其他模型(例如用户),但仍然像只有一个一样使用它们,并尝试按类型区分它们。

我的项目设计为只使用一个地址,所以我有很多类似的代码,User.first.address或者User.first.address=...我无能为力。

这是一个例子:

|id|addressable_type|addressable_id| type   | street |
  1|User            |1             | first  | Foo
  2|User            |1             | second | Bar  
  3|User            |1             | NULL   | Other  

我想做的是:

User.find(1).address(:second)

哪个应该返回<Address street:"Bar">

User.find(1).address

哪个应该返回<Address street:"Other">,但User.find(1).addresses应该返回对象集合。

我使用一段代码为我的模型添加地址行为:

    module Ext::Models::Addressable

def self.included(mod)
    super
    mod.class_eval do
      def self.acts_as_addressable          
        include InstanceMethods

        # Use has_many :delete_all for better performance
        has_many :addresses, :as => :addressable, :dependent => :delete_all
        has_one :address, :as => :addressable
      end
    end
end

module InstanceMethods
            # I tried with this, but this doesn't even allow me to start rails becauses raises "there is no address method"  
    def address_with_type(type = nil)
        if type
            self.addresses.find(:first, :conditions => ["type = ?", type.to_s])
        else
            address_without_type
        end
    end
    alias_method_chain :address, :type
end
end

我正在使用 Rails 2.3.14 和 Ruby 1.9.2。

如果这是可能的,我还想知道为给定模型创建新地址的最佳方法(比如说用户)。

提前致谢。

解决方案

我缩小了所有可能性,目前在项目中只有两种类型的地址。我能够大大简化我的方法。目前我的self.included样子:

has_many :addresses, :as => :addressable, :dependent => :delete_all
has_one :address, :as => :addressable, :conditions => {:type => nil}
has_one :foo_address, :class_name => 'Address', :as => :addressable, :conditions => {:type => "foo"}

现在我能够像这样构建新对象: user.build_address(:street => "abc")而且user.build_foo_address(:street => "def"),我需要什么。

4

1 回答 1

0

我会小心以这种方式覆盖默认和预期行为。有你不能放手的理由user.address吗?

我不确定您正在解决什么问题,但也许您可以执行以下操作:

class User < ActiveRecord::Base
  has_many :addresses, :as => :addressable, :dependent => :delete_all

  def address(only_type = "primary")
    addresses.find_first("type" => only_type)
  end

  def address=(addr)
    addresses.delete address
    addresses << addr.update_attribute("type", "primary")
  end

  def build_address(attr)
    addresses.delete address
    addresses.build attr.merge("type" => "primary")
  end

  def create_address(attr)
    addresses.delete address
    addresses.create attr.merge("type" => "primary")
  end
end

我还没有测试过这段代码,而且我对 rails 2 不再那么熟悉了,所以只是将它作为灵感。您可以将它包装在一些元数据中以对其进行模块化。顺便说一句,如果您使用“类型”,您会得到 STI,不确定这是否是您想要的。

于 2012-10-23T22:15:57.330 回答