0

我有一个模式,它看起来是这样的:定义“的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_paymentsPastPayment 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 } 

问题用那种方法?

回答

1

您可以利用此宝石中的ActiveRecord更好地处理“复合主键”:https://github.com/composite-primary-keys/composite_primary_keys

class Customer < ActiveRecord::Base 
    has_many :past_payments, foreign_key: :customer_number, primary_key: :customer_number 

    has_many :payment_details, through: :past_payments # this works now 
end 

class PastPayment < ActiveRecord::Base 
    has_one :payment_detail, foreign_key: [:transaction_date, :arbitrary_sequence_number], 
          primary_key: [:transaction_date, :arbitrary_sequence_number] 
end