has_one从许多基于参数中选择

时间:2012-10-23 13:59:10

标签: ruby-on-rails ruby activerecord

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

我的项目只使用一个地址,因此我有很多代码,例如User.first.addressUser.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"),我需要的是什么。

1 个答案:

答案 0 :(得分: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,不确定这是不是你想要的。