Drahcir
Drahcir

Reputation: 12621

Rails belong_to has_many with a custom foreign key

I have a relationship between 2 models in my rails application. I have veered away from the standard of how to implement the relationship as I used another field as a primary key and the naming convention is different. Doing so resulted in the relationship seemingly not being established. I want to understand as to why.

This is a trimmed down version of my models:

class Player < ActiveRecord::Base
  set_primary_key "alias"
  attr_accessible :alias, :avatar
  has_many :player_sessions, :foreign_key => "player_alias", :class_name => "PlayerSession" 
end

class PlayerSession < ActiveRecord::Base
  attr_accessible :player_alias, :total_score
  belongs_to :player, :foreign_key  => "player_alias", :class_name => "Player" 
end

The Player model has the field alias which is the username in my application. I wanted the username to act as the primary key since it is unique and it would be easier to migrate the data and maintain relationships.

Originally I only had PlayerSession model with data already filled in, but as my application grew I added the Player model and simply inserted a row with the same alias.

In the Player's show view I have the following code:

Player Sessions:
<% @player.player_sessions do |player_session| %>
<ul>
    <li><h4>Highest Score:</h4> <%= player_session.total_score %> </li>
</ul>

When I try to access the page it simple doesn't show the information.

Other information I can add is that I haven't added any relationships in the database itself.

I am still new to rails and still playing around with it. Any opinions that concern coding standards(outside from answering the question) are welcome.


Update I have implemented Babur Usenakunov's suggestion by adding the primary_key option in the models:

class Player < ActiveRecord::Base
  set_primary_key "alias"
  attr_accessible :alias, :avatar
  has_many :player_sessions, :primary_key => "alias", :foreign_key => "player_alias", :class_name => "PlayerSession" 
end

class PlayerSession < ActiveRecord::Base
  attr_accessible :player_alias, :total_score
  belongs_to :player, :primary_key => "alias", :foreign_key  => "player_alias", :class_name => "Player" 
end

Also to test out the the data is valid I acquired the PlayerSession list manually:

Code implemented in controller:

@player_sessions =  PlayerSession.where("player_alias = ?", params[:id])

Code implemented in view(which outputs the data):

<% @player_sessions.each do |player_session| %>
<ul>
    <li><h4>Highest Score:</h4> <%= player_session.total_score %> </li>
</ul>
<% end %>

Upvotes: 33

Views: 49384

Answers (2)

Drahcir
Drahcir

Reputation: 12621

I have solved the issue, it was a matter of adding each in the loop I implemented in the view:

<% @player.player_sessions.each do |player_session| %>
<ul>
    <li><h4>Highest Score:</h4> <%= player_session.total_score %> </li>
</ul>    
<% end %>

After playing around a bit, I realised i didn't need to add the primary_key option in either of the views, and left the foreign key option only in the Player model.

class Player < ActiveRecord::Base
  set_primary_key "alias"
  attr_accessible :alias, :avatar
  has_many :player_sessions, :foreign_key => "player_alias", :class_name => "PlayerSession" 
end

class PlayerSession < ActiveRecord::Base
  attr_accessible :player_alias, :total_score
  belongs_to :player, :class_name => "Player" 
end

Upvotes: 28

marcelowiermann
marcelowiermann

Reputation: 265

If your PlayerSession table has a column named alias then your foreign key should also be alias, not player_alias. As a token of advice I'd be wary of using an alias as a foreign_key: if your player can/decide to change his alias then all PlayerSession records within your database will become invalid and require an update. Using an immutable parameter such as player_id would be preferable to using the existing alias column IMHO.

Upvotes: 3

Related Questions