Search code examples
ruby-on-railsassociationseachnested-attributes

Building new Model A form from Model B attributes


I'm trying to copy the values from Model A nested attributes to Model B nested attributes. The nested fields are the same in their respective tables. Just the parents are different.

I pass the model A value: Model_B_new_path(@routine) and get its value in controller B: @routine = Routine.find(params[:id]), but copying the nested attributes is where I'm stuck.

class RoutineSetRep

    belongs_to :routine

end


class Routine

    has_many :routine_set_reps, dependent: :destroy

    accepts_nested_attributes_for :routine_set_reps, allow_destroy: true

    validates_associated :routine_set_reps
end

class WorkoutSetRep

    belongs_to: :workout

end

class Workout
    has_many :workout_set_reps, dependent: :destroy

    accepts_nested_attributes_for :workout_set_reps, allow_destroy: true

    validates_associated :workout_set_reps
end

Controllers:

   Controller Workout /  Controller B
            def new
            @new_routine_workout = Routine.find_by(id: params[:routine_id])
            @routine = @new_routine_workout
            c = @routine.routine_set_reps.count
            @workout = current_user.workouts.new(@routine.attributes.except('public'))
            c.times{ @workout.workout_set_reps.build }
            end
    
        def workout_params
          params.fetch(:workout, {}).permit(:id, :sport_id, :name, :user_id, :routine_id,
          workout_set_reps_attributes: [:id, :set, :rep, :weight, :distance, :workout_id, :exercise_id,
                                :user_id, :weight_unit, :distance_unit, :_destroy],)
                               
        end
end

I can make the form build the number of fields that should be populated, but aren't.

Would this be a deep clone problem in which I have to create a method that grabs each routine_set_rep? Something like:

    @routine.routine_set_reps.each do |set|
       set.exercise_id = @workout.workout_set_reps.exercise_id 
    end

This does not work but seems to be the right direction. With that said is the idea behind it, is that possible?

When I set the build option to build previous workout records it works fine.

@past_workout = Workout.find_by(id: params[:past_workout_id])
@workout = current_user.workouts.new(@past_workout.attributes.except('public')).copy_workout

The form:

<%= content_tag :div, class: "nested-fields", data: { new_record: form.object.new_record? } do %>
  <div class="form-group">
  <%= form.hidden_field :_destroy %>
  <%= form.collection_select(:exercise_id, Exercise.order(:tool), :id, :name_with_tool, {prompt: nil}, {class: 'form'}) %>
  <%= form.text_field :set, placeholder: "Set", class: "set-control" %>
  <%= form.text_field :rep, placeholder: "Rep", class: "set-control" %>
  <%= form.text_field :weight, placeholder: "Weight", class: "set-control" %>
  <%= form.collection_select :weight_unit, WorkoutSetRep::WEIGHT, :to_s, :titleize, {prompt: nil}, {:class => 'set-control'} %>
  <%= form.text_field :distance, placeholder: "Distance To Run", class: "set-control" %>
  <small><%= link_to "Remove", "#", class: "btn btn--destroy", data: { action: "click->nested-form#remove_association" } %></small>
  </div>
<% end %>

I've stumbled onto these post for help,post post post which they have, but I'm still lost.


Solution

  • First I set the variables, then I was able to iterate over them. Is this is the rails way? I don't know but it works and the max number of objects it would iterate over is 20.

    @routine = Routine.includes(:routine_set_reps).find_by(id: params[:routine_id])
    @workout = current_user.workouts.new(@routine.attributes.extract!('name', 'sport'))
    
    c = r.count
    r = @routine.routine_set_reps     
     w = @workout.workout_set_reps
            c.times{ w.new }
             r.zip(w).each do |routine, workout|
                workout.exercise_id = routine.exercise_id
                workout.set = routine.set
                workout.rep = routine.rep
                workout.weight = routine.weight
                workout.weight_unit = routine.weight_unit
             end
    

    Probably could refactor this into a method and just call it but for now working is working.

    Just fyi I use = instead of ||= because the workout_set_reps will always be new. Is this right? I'm not positive, but leave a comment if you believe differently or agree with a reason.