I have a simple app where people can apply for a position. The table for this is applicants. Then, all users are allowed to rate each applicant. The ratings are in a table called ratings, which just has an applicant_id, user_id and score. So our tables are:

class Applicant < ActiveRecord::Base  
  has_many  :ratings
  accepts_nested_attributes_for :references, :allow_destroy => true

class User < ActiveRecord::Base
  has_many  :ratings

class Rating < ActiveRecord::Base
  belongs_to  :user
  belongs_to  :applicant
  validates_uniqueness_of :applicant_id, :scope => :user_id

The validates_uniqueness_of line is there to make sure that each user can only give one rating for each applicant.

Users edit the applicant to add their score. Since the rating is a nested attribute, we use a fields_for line in the form. Originally, I had something that looked like this:

<%= f.fields_for :ratings, @my_rating do |builder| %>
		<%= builder.collection_select :score, Rating::SCORES, :to_s, :humanize %> 
		<%= builder.hidden_field :user_id, :value => current_user.id %>
	<% end %>

And, in the controller, my edit method looked like this:

def edit
    @applicant = Applicant.find(params[:id])
    @applicant.ratings.build
  end

This worked, in that the ratings field was updated as the form was updated. But there was a problem. When a user went to edit their score (or put in their first score), scores from all users for that applicant showed in the form. This was NOT the behavior I wanted. Users should see only their own rating, if they want to edit it. And if they haven’t yet rated that applicant, they should just see one spot for their rating.

I fixed things by changing the controller to this:

def edit
    @applicant = Applicant.find(params[:id])
    @my_rating = Rating.where(:applicant_id => params[:id]).where(:user_id => current_user.id)
    if @my_rating.empty?
      @my_rating = @applicant.ratings.build
    end
  end

and the form to this:

<%= f.fields_for :ratings, @my_rating do |builder| %>
		<%= builder.collection_select :score, Rating::SCORES, :to_s, :humanize %> 
		1 is the highest ranking, 5 is for candidates to reject
<% if builder.object.user_id.nil?%> <%= builder.hidden_field :user_id, :value => current_user.id %> <% end %> <% end %>

This then works as expected.