0

私は次の設定をしています:

スキーマ.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

ユーザー.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

アドレス.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

ユーザーアドレス.rb:

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

誰かがそうするときuser.primary_billing_address = address、私は結合モデルインスタンスに「請求」を目的として設定してもらいたいです。同様に、shipping と「shipping」も同様です。元。

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必要なアドレスを取得するために使用するリレーションシップの条件を省略しました。この問題はそれとは独立していると思います。

4

1 に答える 1

1

まず、関連付けが少しずれており、両方ともprimary_shipping_address同じprimary_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

アドレスを保存しながら目的を保存するには、2 つのオプションがあります。

オプション 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
于 2013-09-24T01:40:19.187 に答える