Reputation: 1435
I've got a create action that is attempting to create a Rating and a Programme in one go:
def create
@rating = current_user.ratings.create(params[:rating])
@rating.create_programme(params[:programme])
redirect_to ratings_path
end
In this code, a rating belongs to both a user and a programme and a user
has_many :ratings
has_many :programmes, :through => :ratings
and a programme
has_many :users, :through => :ratings
has_many :ratings
When I call the create action above in the RatingsController, the Programme is not being saved as being owned by the @rating for some reason. So if I call for instance:
rating.programme.channel
on a rating in a view, it tells me that the programme is a nil object. However, the programme has been saved fine - it's just the association that hasn't been saved. I'm sure it's a pretty basic thing here, but I can't figure it out. Can someone point me in the right direction?
thanks, a
Upvotes: 1
Views: 1145
Reputation: 187312
In response to your question to help make the code cleaner:
def create
@rating = Rating.new(params[:rating])
@rating.user = curent_user
@rating.programme = Programme.find_or_create_by_title(params[:programme])
@rating.save
redirect_to ratings_path
end
First of all you call find_or_create for a title and then save it, but the record will aleady be created there so the save doesn't do anything at all. Second, while the association proxies are cool for easily creating ralated objects, they can get quite hairy in a more comlex relationship like you have here and make the code harder to read.
So instead of using the association proxies to create records, direct assignment would be better. Its easier to tell at a glance exactly what is happening and where the information is coming from, and doesn't need an ugly merge call in there. It's a little longer, but I thinks its far more easily understood at a glance.
Lastly, you probably don't need @programme
as a stand alone instance variable since you would have easy access to that object from @rating.programme
in your views. In most cases, it's best to pass as few instance variable as possible, especially when the objects have an easily accessible direct relationship. Its especially true in this case becuase you aren't rendering a template at all.
Upvotes: 2
Reputation: 1435
I got the create action working ok with the following. Would appreciate it if anyone could et me know whether there's a more elegant way to get this to work :)
def create
@programme = Programme.find_or_create_by_title(params[:programme])
@programme.save
@rating = current_user.ratings.create!(params[:rating].merge(:programme_id => @programme.id))
redirect_to ratings_path
end
andy
Upvotes: 0
Reputation: 1208
Well you are attempting to create the parent from the child (ratings belongs to Programme, right?) . I don't think that works.
Programme.ratings.create would work.
Upvotes: 0
Reputation: 187312
Your code looks right. Try this. Immediately after hitting this create action go into you script/console and see whats going on. Type:
Rating.last
It should return the rating that was just created. Look for user_id
and programme_id
in the output and see if they are set. If they are then you have a bug elsewhere. Perhaps you are not looking at the rating you think you are or something.
Upvotes: 0