Creating an object from the show action of another object in Rails - ruby-on-rails

So I have a fairly typical blog application with posts and comments.
Each comment belongs to one post
A post can have many comments.
Basically I want to add a form for comments to the show action for posts, without having post_id under attr_accessible in the comment model.
In my posts controller I have:
def show
#post = Post.find(params[:id])
#poster = "#{current_user.name} #{current_user.surname} (#{current_user.email})"
#comment = #post.comments.build( poster: #poster )
end
I'm not entirely sure what I should be doing in the comments controller (I'm not confident that the code above is right either if I'm honest). At the moment I have:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:post])
if #comment.save
redirect_to #post, notice: "Comment posted"
else
redirect_to #post, error: "Error!"
end
end
My routes:
resources :comments
resources :posts do
resources :comments
end
and finally the form:
<%= form_for #post.comments.build do |f| %>
<%= f.label :content, "WRITE COMMENT" %>
<%= f.text_area :content, rows: 3 %>
<%= f.hidden_field :post_id, value: #post.id %>
<%= f.submit "Post" %>
<% end %>
The problem here is that I have no way of passing my post_id from the show action of the posts controller to the create action of the comments controller. Any help is much appreciated. Thank you in advance!

Your posts controller looks fine... but assuming your routes looks like
resources :posts do
resources :comments
end
then your CommentsController#create should/could look like:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:comment])
if #comment.save
redirect_to #post, notice: "Comment posted"
else
redirect_to #post, error: "Error!"
end
end
And your form:
<%= form_for [#post, #comment] do |f| %>
<%= f.hidden_field :poster, value: #poster %>
<%= f.label :content, "WRITE COMMENT" %>
<%= f.text_area :content, rows: 3 %>
<%= f.submit "Post" %>
<% end %>

I will assume that your post model has_many comments and comment belongs_to post
than you in your routes file you can do something like this
resources :posts do
resources :comments
end
this will give you a url schem such as
/posts/:post_id/comments , allowing you to always have post_id of the comment parrent

The URL for your show post should be like post/show/(:id).
Now, in the comment form, you can place a hidden field, with value of params[:id].
hidden_field(:post_id, :value => params[:id])
When you submit your form, you can get the value of post_id using the hidden field.
def create
#comment = Comment.new(params[:comment])
#comment.post_id = params[:post_id]
if #comment.save
flash[:notice] = 'Comment posted.'
redirect_to post_path(#comment.post_id)
else
flash[:notice] = "Error!"
redirect_to post_path(#comment.post_id)
end
end

Related

Same redirect_to route that worked for post does not work for comment (same url)

I have the following post controller:
def create
#book = Book.find(params[:book_id])
#post = #book.posts.create(post_params)
if #post.save
redirect_to book_path(#book), notice: "Success!~"
else
redirect_to book_path(#book), alert: "Failure!"
end
end
Exactly same redirect_to is used for comments. Comment creation form and list are on the same url as the post creation form and list (which is show.html.erb for book)
comments controller create:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(comment_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to book_path(#book), notice: "Success!~"
else
redirect_to book_path(#book), alert: "Failure!"
end
end
But when I create a comment, this error shows: No route matches {:action=>"show", :controller=>"books", :id=>nil}, missing required keys: [:id]. The comment is created and saved in the database.
I have tried book and book.id instead of #book. None worked. (interestingly, from books list to show.html.erb, I can only go there by book_path(book.id) and not book_path(#book)).
Here's my book show action, and below it is my show.html.erb for book.
#book = Book.find(params[:id])
#post = #book.posts.new
#comment = Comment.new
show.html.erb:
<%= form_for([#book, #book.posts.build]) do |form| %>
<p>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit "Post"%>
</p>
<% end %>
<% #book.posts.each do |post| %>
<p>
<%= #book.title %>
<%= post.text %>
</p>
<%= form_for(post.comments.build, url: "/posts/#{post.id}/comments") do |form| %>
<p>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit "Post comment"%>
</p>
<% end %>
<% end %>
routes:
resources :users do
resources :books, shallow: true
end
resources :books do
resources :posts, shallow: true
end
resources :posts do
resources :comments, shallow: true
end
The error says it all, you are not supplying an id for the book. Notice how the show route is something like /books/2 where 2 is the id? That number is not getting supplied in the comments controller. It looks to me like comments belong to a post which belongs to a book, so this should solve the issue for you.
if #comment.save
redirect_to book_path(#post.book.id), notice: "Success!~"
else
redirect_to book_path(#post.book.id), alert: "Failure!"
end
In your code you are using #book but it does not look like you ever set that variable with any values the way you do in the create method, so there is no id value there, make sense?

Ruby on Rails blog and adding comments to posts and editing and deleting comments

I'm working on a ROR blog and have encountered some issues along the way. I'm currently learning Rails and just feel completely lost with connecting all the pieces. I've been working on my comments section for days and was finally able to create comments on posts, but I can't edit or delete them. I also referenced the SO questions below but am still running into problems.
Add Comment to User and Post models (Ruby on Rails)
Here's my layout:
Comment model params:
body \ user_id \ post_id
Model associations:
user.rb
has_many :posts
has_many :comments
post.rb
belongs_to :user
has_many :comments
comment.rb
belongs_to :user
belongs_to :post
routes.rb:
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
get '/' => 'users#index'
get '/posts' => 'posts#index'
post '/posts/create' => 'posts#new'
post '/posts/edit' => 'posts#edit'
get '/signin' => 'sessions#new', as: :new_session
post '/create-session' => 'sessions#create', as: :create_session
get 'signout' => 'sessions#destroy', as: :destroy_session
resources :users
resources :posts
resources :comments
end
comments controller:
class CommentsController < ApplicationController
def index
#comment = Comment.all
end
def new
user = session[:user_id]
#comment = Comment.new(post_id: params[:post_id])
#post = Post.find(params[:post_id])
end
def create
#comment = Comment.new(comment_params)
#comment.user_id = session[:user_id]
#postid = params[:id]
if #comment.save
flash[:notice] = "comment created."
redirect_to '/posts'
else
flash[:error] = "Error creating comment."
redirect_to '/posts'
end
end
def edit
#post = Post.find(params[:id])
end
def update
#comment = Comment.find_by_id(params[:id])
#comment.update(comment_params)
flash[:notice] = "Comment updated."
redirect_to '/posts'
end
def destroy
#comment = Comment.find(params[:comment_id])
#comment.destroy
redirect_to '/posts'
end
private
def comment_params
params.require(:comment).permit(:body, :user_id, :post_id)
end
end
Posts show.html.erb page in views/posts folder:
<%# show all posts %>
<div id="single-post">
<h1>User - <%= #post.user.username %></h1>
<h2>Post - <%= #post.body %> </h2>
<%= link_to("Edit Post", edit_post_path(#post)) %>
</br>
<%= link_to("Delete Post", #post, method: 'delete') %>
</br>
<%= link_to("Add Comment", new_comment_path(post_id: #post.id)) %>
<%#<%= link_to("Edit Comment", edit_comment_path(post_id: #post.id, comment_id: #comment.id))%>
</div>
<h3><% #post.comments.reverse.each do |c| %> </h3>
<div id="single-comment">
<h4>Comment</h4>
<h5>From - <%= c.user.username %></h5>
<h6><%= c.body %> </h6>
</br>
<%= link_to("Edit Comment", edit_comment_path(#post.id)) %>
</br>
<%= link_to("Delete Comment", comment_path(#post.id), method: :delete) %>
</div>
<% end %>
</div>
new.html.erb form in views/comments folder
<div id="comment-form">
<%= form_for #comment do |f| %>
<%= f.label :body %>
<%= f.text_area :body, class: "text-area" %>
<%= f.hidden_field :post_id %>
<%= f.submit %>
<% end %>
</div>
Again I can add comments to posts. When I hover over the edit tag on the comment I'm seeing this: localhost:3000/comments/72/edit
I see this error when I click on edit
When I hover over the delete button I see this: localhost:3000/comments/72
I see this error when I click on delete
I'm at the point where I'm completely lost and feel I have tried everything possible but nothing seems to work. Please help! Here's the GitHub repo as well: https://github.com/angelr1076/rails-blog
The First argument in form cannot contain nil or be empty is telling you that #comment in <%= form_for #comment do |f| %> is nil. This is because in the edit action of your CommentsController you are setting #post instead of #comment.
Change this to be:
def edit
#comment = Comment.find(params[:id])
end
For deleting a comment, the Couldn't find Comment without an ID is telling you that the value you're passing to find is nil. This is because you're trying to use params[:comment_id] instead of params[:id]. Change the destroy action to:
def destroy
#comment = Comment.find(params[:id])
#comment.destroy
redirect_to '/posts'
end
Update:
Also as per your code, you should change edit and delete links to below
<%= link_to("Edit Comment", edit_comment_path(c)) %>
<%= link_to("Delete Comment", comment_path(c), method: :delete)
You are passing #post.id which is an id of post. Instead you should pass id of the comment using the block variable from your comments.each, noticing that the .id isn't needed here because it can be inferred by Rails.

Rails: User form_for for a post does not work

I'm trying to render a text area on a user's profile page to allow them to create a post. At the moment I can view all of a user's posts, but I can't get to their profile page because the form will not render.
I did try to changing some stuff around, but then the form would not post due to a routing error.
I feel like I'm posting to the wrong path but I'm not sure so and advice would be appreciated.
class PostsController < ApplicationController
def index
#posts = Post.all
end
def new
#post = Post.new
end
def create
#post = current_user.posts.build(post_params)
if #post.save
flash[:success] = "Posted!"
redirect_to root_url
else
flash[:notice] = "Post could not be submitted"
redirect_to users_path
end
end
private
def post_params
params.require(:post).permit(:user_id, :content)
end
end
_post_form.html.erb
<%= form_for :post, user_posts_path, {method: "create"} do |f| %>
<%= f.text_area :content, size: "60x12", placeholder: "What do you want to say?" %>
<%= f.submit "Post" %>
<% end %>
There's no such HTTP verb called create, you need to change the method mentioned in your form_for, instead of this:
<%= form_for :post, user_posts_path, {method: "create"} do |f| %>
try this:
<%= form_for :post, user_posts_path, {method: "post"} do |f| %>
Please check rake routes and I think you provided the
resources :users do
resources :posts
end
in routes.eb
so
<%= form_for :post, user_posts_path(#user), html: { method: "post"} do |f| %>
in controller
def new
#post = Post.new
#user = current_user
end

Rails 4. Set up comment editing functionality with nested resources

I have the following models: User, Product and Comment. The user can add, edit and delete comments to the product. I've successfully set up adding and deleting functionality and now I'm struggling with editing, for some reason, it caused me a number of difficulties.
My current code returns this error when I click on the edit comment link:
NoMethodError at /products/800/comments/8/edit
undefined method `comments' for nil:NilClass
Here's how my comment model looks like:
# id :integer not null, primary key
# body :text
# created_at :datetime not null
# updated_at :datetime not null
# user_id :integer
# product_id :integer
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :product
# validations ...
end
In User model I have has_many :comments and in Product - has_many :comments, dependent: :destroy.
In my routes.rb file I have the following nested resources:
resources :products, only: [:show] do
resources :comments, only: [:create, :edit, :update, :destroy]
end
My ProductsController has the only method show and nothing else, and here's how it looks like:
def show
product = Product.find(params[:id])
photos = ProductsPhoto.where(product: product)
case product.products_category.name
when 'Violin'
#product = [product, Violin.where(product: product).first, photos]
when 'Guitar'
#product = [product, Guitar.where(product: product).first, photos]
when 'Saxophone'
#product = [product, Saxophone.where(product: product).first, photos]
when 'Piano'
#product = [product, Piano.where(product: product).first, photos]
end
#comment = Comment.new
#comments = Comment.where(product_id: product.id).order('created_at DESC')
end
And now here's my CommentsController, which has create, edit, update and destroy:
class CommentsController < ApplicationController
def create
#product = Product.find(params[:product_id])
#comment = #product.comments.create(comment_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to #product, notice: 'Comment Created!'
else
redirect_to #product, notice: 'Something went wrong...'
end
end
def show
end
def edit
#product = Product.find(params[:product_id])
#comment = #product.comments.find(params[:id])
end
def update
#product = Product.find(params[:product_id])
#comment = #product.comments.find(params[:id])
respond_to do |format|
if #comment.update_attributes(comment_params)
format.html do
redirect_to [#comment.product, #comment], notice: 'Comment Updated!'
end
else
format.html { render action: 'edit', notice: 'Something went wrong...' }
end
end
end
def destroy
#product = Product.find(params[:product_id])
#comment = #product.comments.find(params[:id])
#comment.destroy!
redirect_to #product, notice: 'Comment Deleted!'
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
My _form view is located at views/products/_form.html.erb and looks like this:
<%= simple_form_for([#product[0], #product[0].comments.build]) do |f| %>
<%= f.error_notification %>
<%= f.input :body, required: true, placeholder: 'Type in your comment...', input_html: { class: 'form-control'}, label: false %>
<%= f.button :submit, class: 'btn btn-primary btn-block' %>
<% end %>
And in the views/products/show.html.erb I render the partial of comments and there are the links for destroying and editing the comment, they look like this:
<%= link_to edit_product_comment_path(comment.product, comment) do %>
<span class="glyphicon glyphicon-pencil"></span>
<% end %>
<%= link_to [comment.product, comment], method: :delete do %>
<span class="glyphicon glyphicon-remove"></span>
<% end %>
the delete link works fine and the edit link doesn't.
Maybe I've mistaken with routes, here're the routes for comments:
product_comments POST /products/:product_id/comments(.:format) comments#create
edit_product_comment GET /products/:product_id/comments/:id/edit(.:format) comments#edit
product_comment PATCH /products/:product_id/comments/:id(.:format) comments#update
PUT /products/:product_id/comments/:id(.:format) comments#update
DELETE /products/:product_id/comments/:id(.:format) comments#destroy
In my views/comments/edit.html.erb I render the same form:
<%= render 'products/form' %>
however, when I click on the edit link, I get the following error:
NoMethodError at /products/800/comments/8/edit
undefined method `comments' for nil:NilClass
at the very first line of the _form.html.erb.
I hope I've provided enough information to describe the problem.
So, could you please help me with resolving that issue with comments editing?
It's too early this morning...
This is not correct :
<%= simple_form_for([#product, #product.comments.build]) do |f| %>
Try :
<%= simple_form_for [#comment.product_id, #comment], url: product_comment_path(#comment.product_id, #comment) do |f| %>
Added section
(All of this is assuming that we are working with a single #comment, not the #comments collection. Based on your comments about the delete working.)
To reply to your comment, yes, absolutely you do need separate forms for "new" versus "edit" action for your child table, comments.
Form "new" naming convention is comments/_form.html.erb, the "edit" form would be comments/_form_edit.html.erb,
I would open the "new" action form for comments...
<%= simple_form_for([:product, #comment]) do |f| %>
And the "edit" action...
<%= simple_form_for [#comment.product_id, #comment], url: product_comment_path(#comment.product_id, #comment) do |f| %>
Sorry I forgot the URL on the previous version, I have updated the code snippet above to reflect this change as well. Caveat: There may be other ways to construct this link, but this is how I have implemented the case of having a product (parent) with reviews/comments (many children).
Nested attributes
In further response to your comment, I believe some of what you're looking for could be achieved by using nested attributes in forms. This is another topic. I would get the new/edit form simple cases working and then add complexity.

Rails Associations (belongs_to, has_many) can't save 2 ids in table with a create method (user, post, comment)

Trying to write a basic "blog-like" app in rails 3, I'm stuck with associations. I need the create method save the post_id as well as the user_id in the comment table (which I need in order to retrive all comments written by a user in order to display it)
The app has users (authentication - devise), posts (posted by users - but I'm not sure it matters in my case) and comments (on the posts, posted by users).
the comment table has a post_id, a body, and also a user_id
Associations:
has_many :comments (In the Post model)
belongs_to :post (In the Comment model)
belongs_to :user (In the Comment model)
has_many :comments (In the User model)
the routes:
resources :posts do
resources :comments
end
resources :users do
resources :comments
end
The comment post form displayed on the posts show view: (posts/show.html.erb)
<% form_for [#post, Comment.new] do |f| %>
<%= f.label :body %>
<%= f.text_area :body %>
<%= f.submit %>
<% end %>
and finally, the create method in the comments controller:
A.) If I write this a post_id is written in the database
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create!(params[:comment])
redirect_to #post
end
B.) If I write this a user_id is written...
def create
#user = current_user
#comment = #user.comments.create!(params[:comment])
redirect_to #post
end
I tried:
#comment = #post.comments.create!(params[:comment].merge(:user => current_user))
But it doesn't work.. How can I write a method which save the user_id and the post_id ? Did I have also to do some change in the comment post form (something like <% form_for [#post, #user, Comment.new] do |f| %> ?)
Thank you!
To set up something very similar, I've used the following form:
<%= form_for [:place, #comment] do |f| %>
#form fields here
<%= end %>
Then in the comments controller:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:comment])
#comment.user = User.find(current_user.id)
respond_to do |format|
if #comment.save
format.html { redirect_to(#comment.post, :notice => 'Comment was successfully created.') }
else
format.html { render :action => "new" }
end
end
end
That should build the associations properly hopefully! Just as an aside, do you mean for comments to be nested under :users in your routes? If you just want to display all the user's comments on a profile page, you could do something like:
<p>
<b>Comments</b>
<% if #user.comments.empty? %>
No comments to display yet...
<% else %>
<% #user.comments.each do |comment| %>
<p>
<%= link_to "#{comment.post.title}", post_path(comment.post_id) %>, <%= comment.created_at %>
<%= simple_format comment.content %>
</p>
<% end %>
<% end %>
</p>
Hope some of that helps!

Resources