2013-09-24 49 views
0

我有以下設置:分配過程中添加屬性聯接模型父模型

Schema.rb

ActiveRecord::Schema.define(version: 20130923235150) do 

    create_table "addresses", force: true do |t| 
    t.datetime "created_at" 
    t.datetime "updated_at" 
    end 

    create_table "user_addresses", force: true do |t| 
    t.integer "user_id" 
    t.integer "address_id" 
    t.string "purpose" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
    end 

    create_table "users", force: true do |t| 
    t.datetime "created_at" 
    t.datetime "updated_at" 
    end 

end 

User.rb:

class User < ActiveRecord::Base 
    has_one :user_address 
    has_one :primary_shipping_address, through: :user_address, class_name: :UserAddress, source: :address 
    has_one :primary_billing_address, through: :user_address, class_name: :UserAddress, source: :address 
end 

Address.rb:

class Address < ActiveRecord::Base 
    has_one :user_address 
    has_one :primary_shipping_user, through: :user_address, class_name: :UserAddress, source: :user 
    has_one :primary_billing_user, through: :user_address, class_name: :UserAddress, source: :user 
end 

UserAddress.rb:

class UserAddress < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :address 
end 

當有人做user.primary_billing_address = address,我想加入模型實例有「結算」設置爲宗旨。與運輸和「運輸」類似。防爆。

irb(main):013:0> u = User.new 
=> #<User id: nil, created_at: nil, updated_at: nil> 
irb(main):014:0> a = Address.create 
=> #<Address id: 3, created_at: "2013-09-24 00:13:07", updated_at: "2013-09-24 00:13:07"> 
irb(main):015:0> u.primary_billing_address = a 
=> #<Address id: 3, created_at: "2013-09-24 00:13:07", updated_at: "2013-09-24 00:13:07"> 
irb(main):016:0> u.save! 
=> true 
irb(main):017:0> u.user_address 
=> #<UserAddress id: 2, user_id: 3, address_id: 3, purpose: nil, created_at: "2013-09-24 00:13:18", updated_at: "2013-09-24 00:13:18"> 
(not what I want... purpose should be "billing") 

我怎麼能做到這一點,使得它適用於新堅持記錄?我已經提出了90%的解決方案,但由於我的方法沒有捕捉到邊緣情況,因此打破了一些隨機規範。

解決問題最棘手的部分是association=的行爲方式:在新記錄中,它通過連接模型對關聯進行排隊。 PS:我忽略了has_one關係的條件,我用它來獲得我想要的地址。我認爲這個問題是獨立的。

+0

我想你會希望定義before_save方法來檢查帳單地址的設置並適當地設置目的? –

+0

我如何知道要分配哪個用途? – Kaleidoscope

回答

1

首先,關聯有點偏離,primary_shipping_addressprimary_billing_address都會返回相同的地址。您可以將其更改爲

class User < ActiveRecord::Base 
    has_many :user_addresses # user can have multiple addresses one for shipping and one for billing 
    has_one :primary_shipping_address, 
      through: :user_address, class_name: :UserAddress, 
      source: :address, :conditions => ['user_addresses.purpose = ?','shipping'] 

    has_one :primary_billing_address, 
      through: :user_address, class_name: :UserAddress, 
      source: :address, :conditions => ['user_addresses.purpose = ?','billing'] 
end 

要在保存地址時保存目的,有兩個選項。

選項1:改寫默認association=方法

# alias is needed to refer to original method 
    alias_method :orig_primary_billing_address=, :primary_billing_address= 
    def primary_billing_address=(obj) 
    self.orig_primary_billing_address = obj 
    self.user_addresses.where("address_id = ?", obj.id).update_attribute(:purpose, 'billing') 
    end 

    # repeat for shipping 

選項2:創建一個自定義的方法(我喜歡這個,因爲它是清潔,DRY)

def save_address_with_purpose(obj,purpose) 
     self.send("primary_#{purpose}_address=", obj) 
     self.user_addresses.where("address_id = ?", obj.id).update_attribute(:purpose, purpose) 
    end