Reputation: 35
I have a nested form set up that will allow me to create up to 7 "Schedule" instances at one time. Each instance will allow a user to assign a schedule_start_time(datetime), schedule_end_time(datetime), and notes(String) field value. When I submit the form with a few of these completed, the parameters array looks exactly as I would expect:
{"utf8"=>"✓", "authenticity_token"=>"HEoylzovRgr7BCZH47iNRPfizDHeVFMLTEmIiNeudcw=", "workout"=>{"id"=>"2", "schedules_attributes"=>{
"0"=>{"scheduled_start_time"=>"06/01/2011", "scheduled_end_time"=>"06/02/2011", "notes"=>"Notes 1"},
"1"=>{"scheduled_start_time"=>"", "scheduled_end_time"=>"", "notes"=>""},
"2"=>{"scheduled_start_time"=>"06/03/2011", "scheduled_end_time"=>"06/04/2011", "notes"=>"Notes 2"},
"3"=>{"scheduled_start_time"=>"", "scheduled_end_time"=>"", "notes"=>""},
"4"=>{"scheduled_start_time"=>"06/16/2011", "scheduled_end_time"=>"06/30/2011", "notes"=>"Notes 3"},
"5"=>{"scheduled_start_time"=>"", "scheduled_end_time"=>"", "notes"=>""},
"6"=>{"scheduled_start_time"=>"", "scheduled_end_time"=>"", "notes"=>""}}}, "commit"=>"Submit"}
In the controller, I filter those "schedules" with a blank start_date. My params list then looks like this:
{"utf8"=>"✓", "authenticity_token"=>"HEoylzovRgr7BCZH47iNRPfizDHeVFMLTEmIiNeudcw=", "workout"=>{"id"=>"2", "schedules_attributes"=>{
"0"=>{"scheduled_start_time"=>"06/01/2011", "scheduled_end_time"=>"06/02/2011", "notes"=>"Notes 1"},
"2"=>{"scheduled_start_time"=>"06/03/2011", "scheduled_end_time"=>"06/04/2011", "notes"=>"Notes 2"},
"4"=>{"scheduled_start_time"=>"06/16/2011", "scheduled_end_time"=>"06/30/2011", "notes"=>"Notes 3"}}},
"commit"=>"Submit", "action"=>"create", "controller"=>"schedules"}
The SQL that gets generaated is not what I would expect however:
(0.1ms) BEGIN WARNING: Can't mass-assign protected attributes: id
SQL (0.2ms) INSERT INTO
schedules
(created_at
,notes
,scheduled_end_time
,scheduled_start_time
,updated_at
,workout_id
) VALUES ('2011-06-29 03:23:45', 'Notes 1', '2011-02-06 00:00:00', '2011-01-06 00:00:00', '2011-06-29 03:23:45', 2)SQL (0.1ms) INSERT INTO
schedules
(created_at
,notes
,scheduled_end_time
,scheduled_start_time
,updated_at
,workout_id
) VALUES ('2011-06-29 03:23:45', 'Notes 2', '2011-04-06 00:00:00', '2011-03-06 00:00:00', '2011-06-29 03:23:45', 2)SQL (0.2ms) INSERT INTO
schedules
(created_at
,notes
,scheduled_end_time
,scheduled_start_time
,updated_at
,workout_id
) VALUES ('2011-06-29 03:23:45', 'Notes 3', NULL, NULL, '2011-06-29 03:23:45', 2) (0.5ms) COMMIT
Some of the valid date values are in the params array, but are being filtered out before the SQL commits.
Here is the controller code:
def create
@workout = Workout.find(params[:workout][:id])
7.times do |count|
@schedule = params[:workout][:schedules_attributes]["#{count}"]
if (@schedule[:scheduled_start_time].blank?)
params[:workout][:schedules_attributes].delete count.to_s.to_sym
end
end
if @workout.update_attributes(params[:workout])
redirect_to schedules_url, :notice => "Successfully updated schedule."
else
render :action => 'new'
end
end
And the Workout Model
class Workout < ActiveRecord::Base
belongs_to :team, :class_name => "Team", :foreign_key => "team_id"
has_many :exercise_instances, :dependent => :destroy
validates :name,
:presence => true
has_many :schedules, :dependent => :destroy
accepts_nested_attributes_for :schedules
end
And the Schedule Model
class Schedule < ActiveRecord::Base
attr_accessible :workout_id, :scheduled_start_time, :scheduled_end_time, :notes
belongs_to :workout
end
Any direction would be welcome. I'm suspecting caching at some level, but I'm just not sure where to start looking. Thanks!
Upvotes: 1
Views: 389
Reputation: 80041
I think you are reinventing the wheel here and it's the source of the problem. Try the following:
class Workout < ActiveRecord::Base
has_many :schedules, :dependent => :delete_all
accepts_nested_attributes_for :schedules, :allow_destroy => true, :reject_if => proc { |schedule| schedule[:scheduled_start_time].blank? }
end
You will still need to restrict the number of schedules to 7. I would recommend checking out existing solutions for this behavior; there are a lot of solid patterns on this already.
Upvotes: 1
Reputation: 2380
Personally I would do something like
params[:workout][:schedules_attributes].each do |sched|
@workout.schedules << @workout.schedules.build(sched) if sched[:scheduled_start_time].present?
end
if @workout.save # etc
And not use nested_attributes_for. This will guarantee that you will only get what was sent.
I've found with nested_attributes that it's often best to delete and recreate on edit every time as well, which may or may not be what you want.
I'm sure others with better nested_attributes 'fu' than me may have better solutions.
Upvotes: 1