2012-10-20 3 views
0

나는 형태로 선택 상자를 채우는 데 내 User 모델에이 기능을 사용하고 있습니다 :데이터베이스 필드가 아닌 가상 속성별로 레코드를 필터링합니까?

def outstanding_invoices 
    invoices.to_a.select { |invoice| invoice.balance < 0 } 
end 

문제는 balance 데이터베이스 필드하지만, 뭔가를 계산하는 방법은되지 않는 것입니다.

user에있는 모든 invoice에 대해 위의 함수가 반복되므로 선택 상자를 생성하는 데 엄청난 양의 SQL 쿼리가 필요합니다.

하나의 SQL 쿼리 만 사용하는 것이 더 빠른 선택 상자를 생성 할 수 있습니까?

간단한 대안은 각 invoicebalance을 데이터베이스에 저장하는 것이지만 데이터베이스를 최소한으로 유지하기를 원하기 때문에 그 일을하기를 꺼립니다.

도움 주셔서 감사합니다.

답변

2

계산 된 금전 값을 사용한 경험을 바탕으로 많은 경우 계산 된 값을 해당 값을 계산하는 데 사용 된 매개 변수와 함께 저장하는 것이 좋습니다. 계산 된 값에 대해 쿼리를 실행할 수 있지만 값이 계산 된 방법에 대한 추적 성이 있어야합니다.

귀하의 인보이스 예에서는 Payment 모델에 after_save 콜백을 구현하는 것이 좋습니다. 예를 들어이 방법으로

class Payment < ActiveRecord::Base 
    belongs_to :invoice 

    def after_save 
    invoice.update_balance 
    end 
end 

class Invoice 
    has_many :payments 

    def self.with_open_balance 
    where("current_balance > 0") 
    end 

    def update_balance 
    update_attributes(:current_balance => balance) 
    end 

    def balance 
    invoiced_amount - payments.sum(:amount) 
    end 
end 

, 이제 해당 고객에 대한 열려있는 모든 청구서를 얻을 수 customer.invoices.with_open_balance를 호출 할 수 있습니다. 지불이 저장 될 때마다 지불이 속한 인보이스는 잔액을 다시 계산하고 계산 된 값을 동일한 데이터베이스 트랜잭션 내에 저장하여 일관성을 보장합니다.

+0

확인해 주셔서 감사합니다. 그냥 이것을 구현하려고합니다. 'update_attributes' 메쏘드는 여기서'stack overlflow'를 발생시킵니다. 이것에 대한 대안이 있습니까? – Tintin81

+0

흠,'update_column (: current_balance, balance)'가 최선의 방법이라고 생각했습니다. ... – Tintin81

+0

'update_attributes'는 정상적으로 작동해야합니다 - 어디서나 (예 : 이름 충돌과 같은) 재귀 적 메서드 호출이 없는지 확인하십시오. 'update_attribute'를 시도해 볼 수도 있지만 다른 의미가 있으므로 문서를 확인하십시오. –

관련 문제