Rails - 重构控制器的嵌套属性创建的问题

时间:2011-09-25 02:24:22

标签: ruby-on-rails ruby-on-rails-3 activerecord refactoring

我有模特:

Transaction has_many :credits, :debits
Credit belongs_to :transaction
Debit belongs_to :transaction

每个信用证必须有平衡借记,反之亦然。

我目前(成功)使用以下内部事务的create方法实现此目的:

@transaction = Transaction.new(params[:transaction])

Transaction.transaction  do

  # Balance debits with credits (Sales)
  if params[:balance_transaction] == 'credit'
    @transaction.credits.each do |credit|
      @transaction.debits.push(Debit.new({
        :quantity => 0,
        :cost_per_unit => 0,
        :description => 'Balancing Debit',
        :amount => credit.amount,
        :account_id => 23 #Get from settings in future to allow users to choose Debtor account
      }))
    end
  elsif params[:balance_transaction] == 'debit'
    @transaction.debits.each do |debit|
      @transaction.credits.push(Credit.new({
        :quantity => 0,
        :cost_per_unit => 0,
        :description => 'Balancing Credit',
        :amount => credit.amount,
        :account_id => 43 #Get from settings in future to allow users to choose Creditor account
      }))
    end
  else
    raise ActiveRecord::Rollback # There's no balancing transaction. Don't save it!
  end

end

我尝试通过替换@transactions.credits.push(...) with debit.balancing_credit并将以下内容放入借记模型中,将平衡借记/贷记创建转移到借记/贷记模型中:

def balancing_credit
  transaction.credits.new({
    :quantity => 0,
    :cost_per_unit => 0,
    :description => 'Balancing Debit',
    :amount => amount,
    :account_id => 43 #Get from settings in future to allow users to choose Creditor account
  })
end

我认为这是非常简单的重构,但它会引发undefined method 'debits' for nil:NilClass错误。似乎它在数据库中查找尚未保存的交易以创建平衡信用额度?我做错了吗?

1 个答案:

答案 0 :(得分:1)

你是对的,这样的机制应该属于模型,而不属于控制器。您可以在Transaction模型上进行before_save回调:

class Transaction
  after_save :balance_operations

  def balance_operations
    if credits
      credits.each do|credit|
        debit = debits.build (#do all your stuff here. build() automaticly fills the transaction_id field of your new debit with the proper id)
        return false unless debit.save
      end
    if debits # can use elsif if mutually exclusive conditions
      # same thing as above
    end
  end
 end

这依赖于回调链包装在ActiveRecord :: Base.transaction中的事实。如果回调返回false,则ActiveRecord将执行回滚。有关详细信息,请参阅“交易”一章 here

如果您在一个交易中有许多操作,您还可以查看a look at this to improve performance

修改:您还可以将validates_associated :debit, :credits添加到模型中