我有一個模式,它看起來是這樣的:定義「的has_many通過:ASSOCATION」在作用域條件語句通過模型
create_table "customers" do |t|
t.integer "customer_number"
end
create_table "past_payments" do |t|
t.integer "customer_number"
t.datetime "transaction_date"
t.integer "arbitrary_sequence_number"
end
create_table "payment_details" do |t|
t.datetime "transaction_date"
t.integer "arbitrary_sequence_number"
end
TL; DR從模式 - 客戶與past_payment通過關聯主/外鍵。當他們的transaction_date和random_sequence_number相等時,PastPayment與單個PaymentDetail相關聯。付款和詳細信息沒有正式的主/外關鍵關係。
這給了我下面的ActiveRecord型號:
class Customer < ActiveRecord::Base
has_many :past_payments, foreign_key: :customer_number, primary_key: :customer_number
has_many :payment_details, through: :past_payments # unfortunately, broken
end
class PastPayment < ActiveRecord::Base
has_one :payment_detail, ->(past_payment) {
where(arbitrary_sequence_number: past_payment.arbitrary_sequence_number)
}, foreign_key: :transaction_date, primary_key: :transaction_date
end
由於Customer has_many :past_payments
和PastPayment has_one :payment_detail
,我會認爲有可以是一個Customer has_many :payment_details, through: :past_payments
來定義這樣的關聯,但我不能得到那個工作因爲has_one :payment_detail
關聯中定義的範圍。
具體而言,撥打電話Customer.payment_details
將產生NoMethodError: undefined method 'arbitrary_sequence_number' for #<Customer:0x2i8asdf3>
。所以看起來客戶已經通過了我的範圍,而不是PastPayment。
是否可以在客戶上定義has_many :payment_details
關聯?難道我做錯了什麼?
爲了清楚起見,我希望能夠說並執行兩個查詢,所以如果有一種方法可以完成那個沒有關聯的問題,我很樂於接受。
注:我無法更改此數據庫。這是其他應用程序寫入的數據庫,我需要從中讀取數據。
與我的問題無關,這裏是我目前正在使用的解決方法。如果沒有辦法正確地使用協會,我很高興有這個解決方案批評:
class Customer < ActiveRecord::Base
attr_writer :payment_details
def payment_details
@payment_details ||= Array(self).with_payment_details.payment_details
end
module InjectingPaymentData
def with_payment_details
results = self.to_a
return self unless results.first.is_a?(Customer)
user_ids = results.collect(&:id)
# i've omitted the details of the query, but the idea is at the end of it
# we have a hash with the customer_number as a key pointing to an array
# of PaymentDetail objects
payment_details = PaymentDetails.joins().where().group_by(&:customer_number)
results.each do |customer|
customer.payment_details = Array(payment_details[customer.customer_number])
end
end
end
end
ActiveRecord::Relation.send(:include, Customer::InjectingPaymentData)
Array.send(:include, Customer::InjectingPaymentData)
有了這樣的,我可以做的事情一樣以最小的查詢如下:
@customers = Customer.where(id: 0..1000).with_payment_details
@customers.each { |c| do_something_with_those_payment_details }
問題用那種方法?