0

I'm building a simple top-to-bottom Workout Routine app on ROR. I'm able to create a Workout Day (parent) and an Exercise (child) on the same form. But I can't seem to save the Weighted Set (grandchild) when I submit the form. The interesting thing is that since the Exercise is saved, I can go to that exercise edit page, add a Weighted Set, and the Weighted Set will show up in the Workout Day show page. I think it has to do with the Weighted Set not being associated with the Exercise at the time of creation. How cam I tie wll three models together? I know I'm close!

I have the whole app on github. I the link isn't working, try this URL https://github.com/j-acosta/routine/tree/association

Models

class WorkoutDay < ApplicationRecord
  has_many :exercises, dependent: :destroy
  has_many :weighted_sets, through: :exercises

  accepts_nested_attributes_for :exercises
  accepts_nested_attributes_for :weighted_sets
end

class Exercise < ApplicationRecord
  belongs_to :workout_day, optional: true
  has_many :weighted_sets, dependent: :destroy

  accepts_nested_attributes_for :weighted_sets
end

class WeightedSet < ApplicationRecord
  belongs_to :exercise, optional: true
end

Workout Day Controller

class WorkoutDaysController < ApplicationController
  before_action :set_workout_day, only: [:show, :edit, :update, :destroy]

  ...

  # GET /workout_days/new
  def new
    @workout_day = WorkoutDay.new

    # has_many association .build method => @parent.child.build
    @workout_day.exercises.build

    # has_many :through association .build method => @parent.through_child.build
    # @workout_day.weighted_sets.build
    @workout_day.weighted_sets.build

  end

  ...

  # POST /workout_days
  # POST /workout_days.json
  def create
    @workout_day = WorkoutDay.new(workout_day_params)

    respond_to do |format|
      if @workout_day.save
        format.html { redirect_to @workout_day, notice: 'Workout day was successfully created.' }
        format.json { render :show, status: :created, location: @workout_day }
      else
        format.html { render :new }
        format.json { render json: @workout_day.errors, status: :unprocessable_entity }
      end
    end
  end

  ...

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_workout_day
      @workout_day = WorkoutDay.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def workout_day_params
      params.require(:workout_day).permit(:title, exercises_attributes: [:title, :_destroy, weighted_sets_attributes: [:id, :weight, :repetition]])
    end
end

New Workout Day form

<%= form_for @workout_day do |workout_day_form| %>
  <% if workout_day.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(workout_day.errors.count, "error") %> prohibited this workout_day from being saved:</h2>

      <ul>
      <% workout_day.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div>
    <%= workout_day_form.label :title, 'Workout Day Name' %>
    <%= workout_day_form.text_field :title %>
  </div>


  exercise_field will go here
  <div>
    <%= workout_day_form.fields_for :exercises do |exercise_field| %>
        <%= exercise_field.label :title, 'Exercise' %>
        <%= exercise_field.text_field :title %>
    <% end %>
  </div>

  weighted_set_fields will go here
  <div>
      <%= workout_day_form.fields_for :weighted_sets do |set| %>
        <%= render 'exercises/weighted_set_fields', f: set %>
      <% end %>  
  </div>

  <div>
    <%= workout_day_form.submit %>
  </div>
<% end %>

1 Answer 1

0

The culprit is the workout_day_params. In the form you have the fields of weighted_sets nested under the workout_day. But in the workout_day_params, you have weighted_sets_attributes under exercises_attributes which is the reason for your problem. Changing it to below should solve the issue.

def workout_day_params
  params.require(:workout_day).permit(:title, exercises_attributes: [:title, :_destroy], weighted_sets_attributes: [:id, :weight, :repetition])
end

ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrMany‌​Reflection

This due to wrong associations. You should consider tweaking your associations like below

class WorkoutDay < ApplicationRecord
  has_many :weighted_sets, dependent: :destroy
  has_many :exercises, through: :weighted_sets

  accepts_nested_attributes_for :exercises
  accepts_nested_attributes_for :weighted_sets
end

class Exercise < ApplicationRecord
  has_many :weighted_sets, dependent: :destroy
  has_many :workout_days, through: :weighted_sets
end

class WeightedSet < ApplicationRecord
  belongs_to :exercise, optional: true
  belongs_to :workout_day, optional: true
end
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you once again. I saw that before and didn't think anything of it. However when I save I get an error ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection in WorkoutDaysController#create other forums say it the way the models are associated. Any ideas?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.