views:

45

answers:

1

Background: I have an application with athletes, each of these athletes (Athlete model) can be assigned workouts (Workout model, workout_assignment through model).

Problem: At first I thought that using a through connection with athletes having many workouts through workout_assignment and workouts having many athletes through workout_assignment would work fine for me. If I did it this way however, if I assigned a workout to 50 athletes, they would all be referencing the same workout record. I want a coach (the person who assigns workouts) to be able to assign the same workout to 50 athletes but then be able to change them one by one if he wishes (customize for the athletes). Does anyone have advice for me on how to approach this? Do I need to create 50 copies of the workout and assign each to a user, do I really need the workout_assignment through model then if I have separate workouts?

Thank you for any advice you can give!

Schema:

  create_table "athletes", :force => true do |t|
    t.string   "name"
    t.string   "username"
    t.string   "password"
    t.string   "sport"
    t.text     "notes"
    t.integer  "coach_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "crypted_password"
    t.string   "password_salt"
    t.string   "persistence_token"
  end

  create_table "coaches", :force => true do |t|
    t.string   "name"
    t.string   "username"
    t.string   "password"
    t.string   "address"
    t.string   "city"
    t.string   "state"
    t.string   "zipcode"
    t.string   "phone"
    t.string   "sports"
    t.integer  "experience"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "crypted_password"
    t.string   "password_salt"
    t.string   "persistence_token"
  end

create_table "workout_assignments", :force => true do |t|
    t.integer  "athlete_id"
    t.integer  "workout_id"
    t.date     "date_assigned"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "workouts", :force => true do |t|
    t.string   "name"
    t.string   "type"
    t.integer  "coach_id"
    t.text     "description"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "category_id"
  end

Model Associations:

class Athlete < ActiveRecord::Base
  belongs_to :coach
  has_many :workouts, :through => :workout_assignments
end

class Workout < ActiveRecord::Base
  has_many :athletes, :through => :workout_assignments
  belongs_to :category
end

class WorkoutAssignment < ActiveRecord::Base
  belongs_to :workout
  belongs_to :athlete
end

class Coach < ActiveRecord::Base
  has_many :athletes
  has_many :workouts
end
+2  A: 

The reason to use a has and belongs to relationship (via reciprocal :through relationship or otherwise) is to reuse the objects on either side.

Have you considered using a rich join model? Your workout_assignments model already seems to be one given the "date_assigned" column. The theory is that your join model includes data specific to that assignment. That way you will only need one copy of each common workout element, and use the workout assignments model to store the individual instructions.

Here's how I would suggest implementing this:

Adapt workout_assignments to include special instructions for the athlete. All you need to do is add a column to the workout_assignments table, I'm going to call it special_instructions but you can call it what ever you want.

In the up method of a new migration:

add_column :workout_assignments, :special_instructions, :string

That's about it. No just be sure to display the special instructions in addition to the workout's description when displaying it to an athlete/coach. There are somethings you can do to ease the transition of referring to workout_assignments in place of workouts.

Have a look into delegation, custom accessors and usage of the :include option on a has_many association. For inspiration on how to make these models work better together. The idea is that you can eager load the associated workout any time you load a workout assignment.

Delegation is a technique that allows you to pass methods to other options. In essence, giving the appearance of merging the two models. In it's simplest form it looks something like this:

def description
  workout.description
end

Then write a the custom accessor for description on workout_assignments that fetches the description for the the associated work out and displays it along side the special instructions of the assignment.

EmFi
I wish I could vote up your answer 100 times... very helpeful, this makes complete sense now and I will work on implementing something like this.Thanks again!
Trevor Nederlof