seanoshea
seanoshea

Reputation: 6136

Rails Model Relationships for Rugby Players

I have two models in my rails 4.2.4 project that I'd like to join together and I'm struggling with creating the correct relationship between the two. The domain in question is rugby and the code is all available at https://github.com/rgbycch/rgbycch-rest/tree/rails. The models are:

Player (https://github.com/rgbycch/rgbycch-rest/blob/rails/app/models/player.rb) PlayerPosition (https://github.com/rgbycch/rgbycch-rest/blob/rails/app/models/player_position.rb)

I'd like to be able to create a relationship whereby a Player can have multiple favoured PlayingPositions. I believe my db structure would need to look something like this:

create_table :favoured_positions do |t|
  t.references :player, index: true, foreign_key: true # fk to player table
  t.references :player_position, index: true, foreign_key: true # fk to player_position table
  t.integer :num_favs # additional data for a favored playing position

  t.timestamps null: false
end

I can generate a new FavoredPosition model like this I guess:

bundle exec bin/rails generate model FavouredPosition player:references player_position:references num_favs:integer

which generates a class that looks like:

class FavouredPosition < ActiveRecord::Base
  belongs_to :player
  belongs_to :player_position
end

I'm not sure how to alter my Player and PlayerPosition models to reflect this new relationship. Is the following correct:

class Player < ActiveRecord::Base
  has_many :favored_positions, :through => :favoured_positions
end

class PlayerPositions < ActiveRecord::Base
  has_many :favored_playing_positions, :through => :favoured_positions
end

Would you advise me to add indexes to either of these tables too?

Thanks,

Sean

Upvotes: 1

Views: 54

Answers (2)

IngoAlbers
IngoAlbers

Reputation: 5802

You would need to set up the associations like this:

class Player < ActiveRecord::Base
  has_many :favoured_positions
  has_many :player_positions, through: :favoured_positions
end

class PlayerPosition < ActiveRecord::Base
  has_many :favoured_positions
  has_many :players, through: :favoured_positions
end

See: http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association

Upvotes: 2

Richard Peck
Richard Peck

Reputation: 76784

Firstly, change PlayerPosition to Position, it's far simpler and works with the DRY (Don't Repeat Yourself) principle.

Secondly, what you're referring to is an ActiveRecord association. These are basically how the ActiveRecord ORM allows you to associate objects in Rails.

--

has_many :through

enter image description here

The association I feel you'd best using is either going to be has_many :through or has_and_belongs_to_many. Since you've comitted to has_many :through, here's what you need to do:

#app/models/player.rb
class Player < ActiveRecord::Base
    #columns id | etc | etc | created_at | updated_at
    has_many :favoured_positions
    has_many :positions, through: :favoured_positions
end

#app/models/position.rb
class Position < ActiveRecord::Base
   #columns id | etc | etc | created_at | updated_at
   has_many :favoured_positions
   has_many :players, through: :favoured_positions
end

#app/models/favoured_position.rb
class FavouredPosition < ActiveRecord::Base
   #columns id | position_id | player_id | any | other | column | created_at | updated_at
   belongs_to :position
   belongs_to :player
end

--

has_and_belongs_to_many

enter image description here

An important caveat about the difference between has_many :through and has_and_belongs_to_many. If you didn't have any extra columns in your favoured_positions join model, I would recommend you use has_and_belongs_to_many. It's far simpler for what you need:

#app/models/player.rb
class Player < ActiveRecord::Base
  has_and_belongs_to_many :positions
end

#app/models/position.rb
class Position < ActiveRecord::Base
   has_and_belongs_to_many :players
end

You'd then make a join table as follows:

#players_positions
player_id | position_id

Upvotes: 1

Related Questions