问题
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