【问题标题】:Object's associated collection always sums to 0 during validation (ActiveRecord)在验证期间对象的关联集合总和为 0 (ActiveRecord)
【发布时间】:2013-09-02 23:35:35
【问题描述】:

我正在记录财务Interactions,建模为偶数个LedgerEntries,每个Interaction 的总和必须为零。

class Interaction < ActiveRecord::Base
  has_many :entries, class_name: 'LedgerEntry'

  validate :entries_must_sum_to_zero

  def balance
    credit = self.entries.sum(:credit)
    debit = self.entries.sum(:debit)
    return credit - debit
  end

  protected

  def entries_must_sum_to_zero
    if self.entries.count.odd?
      errors.add(:entries, "There must be an even number of entries.")
    end
    if self.balance != 0
      errors.add(:entries, "Entries must sum to zero.")
    end
  end
end

class LedgerEntry < ActiveRecord::Base
  validates_numericality_of :credit, greater_than_or_equal_to: 0
  validates_numericality_of :debit, greater_than_or_equal_to: 0
  belongs_to :interaction

  validate :one_of_credit_or_debit

  protected

  def one_of_credit_or_debit
    if self.credit != 0 && self.debit != 0
      errors.add(:credit, "You can't assign both a credit and debit to the one entry.")
    end
  end
end

我的问题是这个测试永远不会失败。

it "should complain if an Interaction's balance is non-zero" do
  d = LedgerEntry.create!(credit: 50.0)
  expect {Interaction.create!(entries: [d])}.to raise_error ActiveRecord::RecordInvalid
end

entries_must_sum_to_zero 执行期间self.entries.count 始终为 0,self.balance 始终返回 0。

如何在验证方法运行之前强制使用条目?

【问题讨论】:

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


    【解决方案1】:

    在您的验证中,您正在使用数据库操作进行验证(即countsum),但条目尚未存储到数据库中,直到@987654323 之后才会存储@ 已保存,它们可以与它们的外键一起存储。

    但是,由于ActiveRecord 代理的魔力,entries 属性仍然可以被访问和操作。你只需要使用不依赖于去数据库的操作,比如length而不是countto_a.map(&amp;:&lt;attribute&gt;).inject(&amp;:+)而不是sum,如:

      def balance
        credit = self.entries.to_a.map(&:credit).inject(&:+)
        debit = self.entries.to_a.map(&:debit).inject(&:+)
        return credit - debit
      end
    

    【讨论】:

    • 在这种情况下,我可能会编写一个工厂方法来创建和后验证对象。
    • 查看更新的答案。在ActiveRecord 中没有办法做到这一点让我很困扰,在玩了一些代码之后,发现了ActiveRecord 代理的神奇世界。
    • 太酷了!我一喝咖啡就试试看
    • 完美运行。并让我在喝咖啡时读到一些有趣的东西。非常感谢。
    • 非常欢迎您。我在调查它时学到了一些有趣的东西。 :-)
    【解决方案2】:

    尝试像这样测试它:

    it "should complain if an Interaction's balance is non-zero" do
      entry = LedgerEntry.new(:credit => 50.0)
      interaction = Interaction.new(:entries => [entry])
      expect {interaction.valid?}.to raise_error ActiveRecord::RecordInvalid
    end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-24
      • 2023-03-23
      • 1970-01-01
      • 2011-05-22
      相关资源
      最近更新 更多