Question

I need to find a way to get rails (3.2.8) to save nested attributes before performing validation on the parent object. I've searched a long time for an answer to my problem and, while I've found similar questions, I haven't yet seen an elegant solution.

Here's the situation:

I have an activity_log where each log_entry has multiple activities. The log_entry has a field called 'hours', which represents the total time worked that day. Each activity also has an 'hours' field, representing time spent on that activity. I need to make sure that log_entry.activities.sum(:hours) is not greater than log_entry.hours.

Here's the code:

class LogEntry < ActiveRecord::Base
  attr_accessible :date, :hours, :activities_attributes

  has_many :activities
  accepts_nested_attributes_for :activities

  validate :sum_hours_not_more_than_total_hours

  private
  def sum_hours_not_more_than_total_hours
    unless activities.sum(:hours) <= hours
      errors.add(:hours, "Hours cannot exceed total hours")
    end
  end
end

The problem is that it checks against the db when calculating activities.sum(:hours) instead of checking against the new data in params. So if I set a log_entry's hours to 4 and create an activity with hours set to 5, it will save with no errors (because the db says that activities.sum(:hours) == 0). But if I edit that same record and set the activity's hours to 4 it will give an error (because the db says activities.sum(:hours) == 5), when it should pass.

I found a workaround here: http://railsforum.com/viewtopic.php?id=41562, but I was hoping there was a more elegant way of solving this problem. (Actually, this solution doesn't work for me as described because it assumes that the only problem is that records are passing validation that shouldn't. I also have the problem that records fail validation when they should pass. I imagine I could fix it by skipping the validation step and then doing a check like sum_hours_not_more_than_total_hours in the controller (after save), but I really like the sense of security I get from validation.)

Was it helpful?

Solution

instead of hitting the database with a sum-query, you could calculate the stuff based of the activities collection like this

self.activities.map(&:hours).sum
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top