I'm creating a sports application which allows users to create pinboards, as many as they like. The users can then add say their favorite player to a specified pinboard. So for example
User: Jack, creates a new pinboard called "My favorite defenders", He can then go and find his favorite defenders, say Kevin Garnet, and add/pin garnet to his "My favorite defenders" Pinboard.
I have a Users model, a Pinboards Model and a Players model. I tried the following association but didn't work as I wanted.
User
has_many :pinboards
has_many :pins, through: pinboards, source: :player
Pinboard
belongs_to :user
belongs_to :player
I know a player can't have many pinboards and a pinboard will hold many players. How can I get the behavior i want; User creates pinboard---user adds player to pinboard.
User
has_many :pin_boards
has_many :pins, through: :pin_boards, source: :players
PinBoard
belongs_to :user
has_many :pin_board_player_assignments
has_many :players, through: :pin_board_player_assignments
PinBoardPlayerAssignment
belongs_to :pin_board
belongs_to :player
Player
has_many :pin_board_player_assignments
has_many :pin_boards, through: :pin_board_player_assignments
So, basically, you'll need to add another model called PinBoardPlayerAssignment
. From there, I'd highly recommend using this join model to create and manage the assignments, like so:
PinBoardPlayerAssignment
def self.create_between(a_pin_board, a_player)
assignment = a_pin_board.pin_board_player_assignments.build
assignment.player = a_player
assignment.save!
end
This way, you can use instance methods on PinBoard
or on Player
to DRYly create the assignment between the PinBoard and the Player. Basically, let the object that knows the most about pin_boards and players be the one to manage the relationships between them.
PinBoard
def add_player(a_player)
PinBoardPlayerAssignment.create_between(self, a_player)
end
Player
def add_to_pin_board(a_pin_board)
PinBoardPlayerAssignment.create_between(a_pin_board, self)
end
And the great thing is that Rails 3.2+ allows nested :through relationships so your original request -- to have @user.pins
should compile the result of going through the pin_board and then through the pin_board_player_assignment table automatically.
Try:
has_many :pins, through: :pin_boards, source: :players
The key is to make the source
be the exact same as the relationship in the join model. So, since your join model (PinBoard) almost certainly says has_many :players
then you have to match that word players
exactly. So you were just missing the 's'.