问题
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.)
回答1:
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
来源:https://stackoverflow.com/questions/17959632/custom-validation-on-nested-attributes