【问题标题】:Defining "has_many through: assocation" with scoped conditionals in the through model在 through 模型中使用作用域条件定义“has_many through:关联”
【发布时间】:2016-08-19 23:11:02
【问题描述】:

我有一个看起来像这样的架构:

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 - 客户通过主键/外键与过去付款相关联。并且当它们的 transaction_date 和 optional_sequence_number 相等时,PastPayment 与单个 PaymentDetail 相关联。 Payments 和 Details 没有正式的主键/外键关系。

这给了我以下 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 #&lt;Customer:0x2i8asdf3&gt;。因此,与 PastPayment 相比,客户似乎正在传递到我的范围。

是否可以在 Customer 上定义 has_many :payment_details 关联?我是不是做错了什么?

为了清楚起见,我希望能够说 Customer.where(some_conditions).includes(: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 }

这种方法有问题吗?

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-4 activerecord associations


    【解决方案1】:

    您可以利用此 gem 更好地处理 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-17
      • 1970-01-01
      • 1970-01-01
      • 2014-05-03
      • 1970-01-01
      • 1970-01-01
      • 2011-10-01
      相关资源
      最近更新 更多