views:

73

answers:

4

I've got a User model that has many Items. A Rating belongs to a User and an Item.

In the DB, I have set ratings.user_id to be not NULL.

when I am creating an Item, I would like to do this:

  def create
    current_user.items.create(params[:item]).ratings.create(params[:rating]
    redirect_to items_path
  end

However, this balks with an SQL error "user_id cannot be nil"

so I rewrote the create method as

  def create
    current_user.items.create(params[:item]).ratings.create(params[:rating].merge({:user_id => current_user}))
    redirect_to items_path
  end

which works fine.

However, I had thought that chaining the create methods off the current user's receiver would have populated the rating's user_id. Anyone know why not?

TIA.

A: 

Because Item has many Ratings and that association does not know about the user id. Given that association chain Item would have a user id because it belongs to a user. And Rating would have an item id because it belongs to an item. But the Item to Rating assocation doesn't know anything about a user unless you tell it.

Squeegy
+1  A: 

I'd recommend you normalize this if possible in the database. Maybe take out the user_id attribute from the ratings table and if you need it in your model get it through a join using a :through method

class Rating
    has_many :items
    has_one :user, :through=>:items
Jeremy
thanks Jeremy. I'll try this out. Bit of a newbie question I realise.
The Pied Pipes
just wondering though: doesn't a Rating belong to an Item, not has many Items?
The Pied Pipes
yes you are right, I didn't think much about that line I just intended to give you the :through example
Jeremy
I would do has_many :items, :through => :ratings... unless there's a reason for that.
Ryan Bigg
+1  A: 

If you created and saved the Item, then made a Rating from that item, it wouldn't pass the user along to the Rating, right? You'd just refer to it as @rating.item.user, right?

When you think about it like that, you wouldn't expect the Item created via the current_user to pass the user information along to the rating.

Make me wonder if you really need the user has_many ratings relationship.

wesgarrison
A: 

Sorry but I have tried to implement the normalising strategy outlined by Jeremy but have hit a wall.

Here's a snippet from a view that calls the items of a current user:

<% for item in @items %>
    <%= item.ratings.find_by_user_id(current_user).value %>
    <%= link_to item.title.capitalize, item.link  %>
  <% end %>

This, since I removed the user_id from Ratings and implemented the new has_many through association, I'm quite confused about how I would now achieve the second line in this view?

Can anyone help me try to rationalise this all out?

a

The Pied Pipes
item.ratings.find(:all, :include => :item, :conditions="item.user_id = #{current_user.id}) will do it I think;also you can use the :through on your user modelclass User has_many :items has_many :ratings => :through=>:itemsThen you can just say current_user.ratings
Jeremy
Thanks Jeremy. If I did what you suggest here, do I have to add some any columns in my model tables to get the associations to work correctly? TIA
The Pied Pipes