bballer320xu
bballer320xu

Reputation: 35

Rails 3.1 - Saving Multiple Models with Dates

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

Answers (2)

coreyward
coreyward

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

Ghoti
Ghoti

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

Related Questions