1

The controller does not update seem to know 'where' a post goes when I change the foreign key (in my case topic_id) via a PUT request.

I am not entirely sure why this happens, but after struggling with this for a few days I really need some insight. This application is supposed to update an object called post and among other things, assign a topic_id. The Topic model is updated via the PostsController via a has_many (posts) belongs_to (topic) relationship.

Facts + Objects are created, edited and destroyed via console and from browser + When I manually change the topic_id the post is visible in the browser

I suspect my controllers are to blame, but not sure.

Question

  • Why is my redirect not working?

Example of Problem:

  1. Consider the following route:localhost:3000/blog/topics/3/posts/1. When I update this record to topic_id = 1 the browser returns the following exception:

    ActiveRecord::RecordNotFound in Blog::PostsController#show

    Couldn't find Post with id=1 [WHERE "posts"."topic_id" = 3]

  2. Not surprising. But, when I actually go to the route localhost:3000/blog/topics/1/posts/1 the object is present. Also, posts objects vanish from the index when the topic is updated-but that is another matter entirely.

  3. I have tried refactoring my redirect in the following ways and they all recreate the same error above:

    • redirect_to blog_topic_post_url([@topic, @post])..
    • redirect_to blog_topic_post_url([@post])..
    • redirect_to blog_topic_post_url(@post)..

When I try the first two redirect_to calls the route redirects to http://localhost:3000/blog/topics/blog/1/3/posts/3, demonstrating the object is storing both topics (1 and 3)???

Object Parameters

{"utf8"=>"✓",
 "_method"=>"put",
 "authenticity_token"=>"TibKWeeC8dGkxR8Jb4bQprGvllUBmiQ5+HtYAPlhn1Q=",
 "post"=>{"title"=>"---- maxime",
 "description"=>"---- et- facere- sunt- et- illo- reprehenderit- dolor- quis- debitis- vel",
 "content"=>"---\r\n- Et asperiores eaque rem maxime laboriosam. Quos dolor perferendis in labore fugit.\r\n  Delectus quam vero optio cum eius perferendis sed.\r\n- Veniam eum explicabo error minima. Dolore reprehenderit cumque reiciendis. Molestiae\r\n  quo est error aut quas ut aperiam quia.\r\n- Et at quo esse aut ut accusantium alias tempore. Accusamus consequuntur sunt mollitia.\r\n  Quas ut voluptate quia sit quia iste corporis. Id rerum placeat voluptas sequi non.\r\n",
    **"topic_id"=>"1",**
    "tags_attributes"=>{"0"=>{"_destroy"=>"0",
    "tag_name"=>"---- velit",
    "id"=>"1"},
    "1"=>{"_destroy"=>"0",
    "tag_name"=>"---- ea",
    "id"=>"2"},
    "2"=>{"_destroy"=>"0",
    "tag_name"=>"---- impedit",
    "id"=>"3"}}},
    "commit"=>"Update Post",
    **"topic_id"=>"3",**
    "id"=>"1"}

Here are my work steps:

Controller

before_filter :fetch_topic,   only: [:show, :edit, :update, :destroy]
before_filter :fetch_post,    only: [:show, :edit, :update, :destroy]

def edit

end

def update 
 respond_to do |format|
  if @topic.update_attributes(params[:post]) and @post.update_attributes(params[:post])
   format.html {redirect_to blog_topic_post_url([@post]), success: 'Post was successfully updated.'}
   else 
    format.html {render :edit, error: 'Post was not updated. Please try again.'}
   end
  end
 end

 # I call these on both actions with before_filter. Works on all other relevant actions.
def fetch_topic
 @topic = Topic.find(params[:topic_id])
end

def fetch_post
  @post =  @topic.posts.find(params[:id]) 
end

form.html.erb

 <%= form_for([:blog, @topic, @post]) do |f| %>
  ...
  ...
 <div class="field">
   <%= f.fields_for(:topic) do |build_topic| %>
    <%= build_topic.label :topic_name, "Select a topic" %>
    <%= collection_select(:post, :topic_id, Topic.all - [@post], :id, :topic_name, prompt: true) %>
   <%end%>

  ....
<div class="actions">
 <%= f.submit %>
</div>
<% end %>
rhodee
  • 1,257
  • 1
  • 14
  • 27

3 Answers3

1

If you observe the update code its redirecting to post url with old topic object. Actually it should updated one that is @post.topic. Also, You dont need to update topic object.

def update 
  if @post.update_attributes(params[:post])
    flash[:success]  = 'Post was successfully updated.'
    redirect_to [@post.topic, @post]
  else 
    flash[:error] = 'Post was not updated. Please try again.'
    render :action => :edit 
  end
 end

One more problem is inside view form, With collection select you dont wanted to list current topic hence it should be Topic.all - @topic

Sandip Ransing
  • 7,583
  • 4
  • 37
  • 48
0

It might be a a problem of ordering, fetch_post require @topic to be set, since both are in before_filter.

Can you post the error?

  • I just posted the error (but not the entire stack trace). But it appears isolated to my routing, because the post object actually saves. – rhodee Feb 24 '12 at 17:24
0

Should you be doing @topic.update_attributes(params[:post])? That seems like it would be problematic. Should it be @topic.update_attributes(params[:post][:topic])?

Also, I think your first redirect is failing because you're using the original Topic you found in fetch_topic, which is no longer associated with the Post. You could probably just explicitly pass the new post topic:

redirect_to blog_topic_post_url([@post.topic, @post])
Brandan
  • 14,735
  • 3
  • 56
  • 71