Accessing parent through unsaved child association (has_many through)

∥☆過路亽.° 提交于 2019-12-12 20:30:20

问题


I have payments with one transaction associated with each payment. The transactions can be associated with payments, deposits, etc. so I need a :through relation.

The Payment class:

class Payment < ActiveRecord::Base
  has_many :payment_transactions
  has_many :transactions, :through => :payment_transactions
end

And the Transaction class:

class Transaction < ActiveRecord::Base
  has_one :payment_transaction, :inverse_of => :transaction
  has_one :payment, :through => :payment_transaction
end

And finally the PaymentTransaction class:

class PaymentTransaction < ActiveRecord::Base
  belongs_to :payment
  belongs_to :transaction
end

When adding the transaction to the payment I would like to be able to access the parent information (the payment attributes) from within the newly created transaction:

2.1.0 :001 > p = Payment.new
 => #<Payment id: nil, state: "new", created_at: nil, updated_at: nil>
2.1.0 :002 > p.save
 => true
2.1.0 :003 > p.transactions.build()
 => #<Transaction id: nil, myid: nil, parent_class_is: nil, created_at: nil, updated_at: nil>
2.1.0 :004 > p.transactions.first.payment
 => nil

I am looking for a way to access the payment from the newly created transaction associated with the payment - but no luck. The payment is saved, however the transaction is still not committed to the database. Only when the payment is saved and the associated transaction is stored I get access to the parent object:

2.1.0 :005 > p.save
2.1.0 :008 > p.reload
2.1.0 :009 > p.transactions.first.payment
=> #<Payment id: 3, state: "new", created_at: "2014-01-28 09:23:13", updated_at: "2014-01-28 09:23:13">

Using the :inverse_of is not possible with :through associations. :(


Edit 1:

Tried to access the parent via a proxy association:

app/concerns/transaction_proxy.rb

module TransactionProxy

  private

  def my_owner
    proxy_association.owner
  end

end

The payment model:

has_many :payment_transactions
has_many :transactions, -> { extending TransactionProxy }, :through => :payment_transactions

Loading the payment:

2.1.0 :006 > p = Payment.first
  Payment Load (0.2ms)  SELECT "payments".* FROM "payments" ORDER BY "payments"."id" ASC LIMIT 1
 => #<Payment id: 1, state: "new", created_at: "2014-01-28 12:31:11", updated_at: "2014-01-28 12:31:11">

Setting the transaction:

2.1.0 :007 > p.transactions.build()
 => #<Transaction id: nil, myid: nil, parent_class_is: nil, created_at: nil, updated_at: nil>

Query the transaction:

2.1.0 :005 > p.transactions.first
 => #<Transaction id: nil, myid: nil, parent_class_is: nil, created_at: nil, updated_at: nil>

2.1.0 :007 > p.transactions.first.payment
 => nil

and trying to use the proxy:

2.1.0 :006 > p.transactions.first.my_owner
NoMethodError: undefined method `my_owner' for #<Transaction:0x00000103e44450>

I assume that the .my_owner on a transaction should return the owner (payment) of that transaction?


回答1:


Your code looks confusing to me!


Polymorphic

I think you'd benefit from a polymorphic association on the transactions model. This will allow your transactions to belong to either payments, deposits or any model

Your use of a through relation means transactions can only be associated with payments and transaction & payment objects have to be created before you can associate them:

class Transaction < ActiveRecord::Base
  belongs_to :transactable, polymorphic: true
end

class Payment < ActiveRecord::Base
  has_many :transactions, as: :transactable
end

class Deposit < ActiveRecord::Base
  has_many :transactions, as: :transactable
end

class Reward < ActiveRecord::Base
  has_many :transactions, as: :transactable
end


Extrabutes

If you want to keep your has_many :through association, you can use the ActiveRecord Association Extensions proxy_association object to retrieve the data you need

We made a script to extra join-table data, which you'll be able to adapt to pull child-object attributes:

#app/models/concerns/image_caption.rb
module ImageCaption

        #Load
        def load
      captions.each do |caption|
              proxy_association.target << caption
      end
        end

        #Private
        private

        #Captions
        def captions
                return_array = []
                through_collection.each_with_index do |through,i|
                        associate = through.send(reflection_name)
                        associate.assign_attributes({caption: items[i]})
                        return_array.concat Array.new(1).fill( associate )
                end
                return_array
        end

        #######################
        #      Variables      #
        #######################

        #Association
        def reflection_name
                proxy_association.source_reflection.name
        end

        #Foreign Key
        def through_source_key
                proxy_association.reflection.source_reflection.foreign_key
        end

        #Primary Key
        def through_primary_key
                proxy_association.reflection.through_reflection.active_record_primary_key
        end

        #Through Name
        def through_name
                proxy_association.reflection.through_reflection.name
        end

        #Through
        def through_collection
                proxy_association.owner.send through_name
        end

        #Captions
        def items
                through_collection.map(&:caption)
        end

        #Target
        def target_collection
                #load_target
                proxy_association.target
        end

end

#app/models/message.rb
has_many :image_messages
has_many :images, through: :image_messages, extend: ImageCaption

If you'd like me to modify this code for you, please ask in the comments!



来源:https://stackoverflow.com/questions/21401629/accessing-parent-through-unsaved-child-association-has-many-through

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!