I finally was able to associate a user model with a post model with:
def create
#post = Post.new(post_params)
#post.user = current_user
Right now I'm trying to create a comment in the Post#Show page but continue to receive an error regarding my form. The two errors I'm running into are:
NoMethodError in Posts#show undefined method `comments_path'
when I have #comment = Comment.new in Post#show. When it's removed I get:
ArgumentError in Posts#show First argument in form cannot contain nil or be empty
What could possibly be wrong with my form? Also if someone can recommend a better way to associate my 3 models (basically have a user_id and a post_id when comment is created I'm open for suggestions). My comment form is going to appear within the Post#Show page. My current code is:
Comment Model
belongs_to :user
belongs_to :post
User Model
has_many :posts
has_many :comments
Post Model
has_many :comments
belongs_to :user
Comment Controller
class CommentsController < ApplicationController
before_action :find_comment, only: [:show, :edit, :update, :destroy]
def index
#comments = Comment.all
end
def new
#comment = Comment.new
end
def create
#post = Post.find(params[:id])
#comment = #post.comments.build(comment_params)
#comment.user = current_user
if #comment.save
flash[:notice] = "Successfully created..."
redirect_to comments_path
else
flash[:alert] = "failed"
redirect_to root_path
end
end
def show
end
def edit
end
def update
if #comment.update
flash[:notice] = "You updated your comment"
else
flash[:alert] = "Failed to update"
end
def destroy
#comment.destroy
redirect_to '/'
end
private
def find_comment
#comment = Comment.find(params[:id])
end
def comment_params
params.require(:comment).permit(:comment)
end
end
Post Controller
class PostsController < ApplicationController
before_action :find_post, only: [:show, :edit, :update, :destroy]
def index
#posts = Post.all
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
#post.user = current_user
if #post.save!
flash[:notice] = "Successfully created..."
redirect_to posts_path
else
flash[:danger] = "failed to add a post"
render 'new'
end
end
def show
end
def edit
end
def update
if #post.update
flash[:notice] = "Successfully updated"
redirect_to post_path
else
flash[:alert] = "Failed to update Post"
redirect_to :back
end
end
def destroy
if #post.destroy
flash[:notice] = "Successfully delete"
redirect_to posts_path
else
flash[:danger] = "Wasn't able to delete Blog post."
redirect_to :back
end
end
private
def find_post
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :body, :description)
end
end
Post#Show View
<%= render '/comments/form' %>
Comment#form
<%= form_for #comment do |f| %>
<%= f.label :comment, "Title" %>
<%= f.text_field :comment, placeholder: "Write a comment" %>
<%= f.submit "Submit" %>
<% end %>
If there is something missing please ask me to update my post. Thank you all who help me better understand my problem.
Your associations seem fine. The error
NoMethodError in Posts#show undefined method `comments_path'
means that you don't have the route comments_path or Comments#Create. Basically, when you have a form_for with a Comment as the parameter, it assumes you are wanting to go to the create or update route, depending if it's a new record. The easiest thing to do would be to add
resources :comments
to your routes file. However, since you want a comment associated with a post, you want to modify your routes file to have:
resources :posts do
resources :comments
end
Then, change your form to look like this:
<%= form_for [#post, #comment] do |f| %>
So when you submit a form, you will have a params[:post_id] and a params[:id] to play with. Find the Post with the params[:post_id]. Ignore the params[:id] when you're creating a comment, but use it when you're updating a comment.
Edit: Here is a link to some help regarding Nested Resourcing.
Related
I'm currently running into a problem with my form.
First argument in form cannot contain nil or be empty
in my comments/new which is a partial as _new.html.erb. This is the file:
<% form_for #comment do |f| %>
<%= f.label :body %>
<%= f.text_field :body, placeholder: "Write Message here.." %>
<% end %>
What I'm trying to do is render add a comment to an Article#Show page.
My code:
Comments Controller
class CommentsController < ApplicationController
before_action :find_comment, only: [:show, :edit, :update, :destroy]
def index
#comments = Comment.all
end
def new
#comment = Comment.new
end
def create
#comment = current_user.comments.build(comment_params)
if #comment.save
redirect_to :back
else
redirect_to '/'
end
end
def show
end
def edit
end
def update
if #comment.update(comment_params)
redirect_to '/'
else
redirect_to :back
end
end
def destroy
#comment.destroy
redirect_to "/"
end
private
def find_comment
#comment = Comment.find(params[:id])
end
def comment_params
params.require(:comment).permit(:body)
end
end
Articles#Show
<%= render '/comments/new' %>
If there is additional code required on my part. Please ask and I'll edit/update the question with additional files.
All help/explanation is appreciated. Thank you in advance.
First question:
Add into Article#Show
def show
#comment = Comment.new
end
So i have added the ability for users to add images to posts in my rails forum. I now want users to be able to add images to comments to posts.
I began with a migration add_attachment_image_to_comments.rb
class AddAttachmentImageToComments < ActiveRecord::Migration
def self.up
change_table :comments do |t|
t.attachment :image
end
end
def self.down
remove_attachment :comments, :image
end
end
Edited the view file:
= simple_form_for([#post, #post.comments.build]) do |f|
= f.input :comment
= f.input :image
%br
= f.submit
Here is the posts_controller file:
class PostsController < ApplicationController
before_action :find_post, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#posts = Post.all.order("created_at DESC").paginate(page: params[:page], per_page: 7)
end
def show
end
def new
#post = current_user.posts.build
end
def create
#post = current_user.posts.build(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def edit
end
def update
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post.destroy
redirect_to root_path
end
private
def find_post
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :content, :image)
end
end
Here is the comments_controller file:
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:comment, :image))
#comment.user_id = current_user.id if current_user
#comment.save
if #comment.save
redirect_to post_path(#post)
else
render 'new'
end
end
def edit
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
end
def update
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
if #comment.update(params[:comment].permit(:comment, :image))
redirect_to post_path(#post)
else
render 'edit'
end
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
end
This give users the ability to add the attachment. The problem i have now is getting the image to show in the comments section on the post.
This is my show file:
#post_content
%h1= #post.title
%p= #post.content
= image_tag #post.image.url(:medium)
#comments
%h2
= #post.comments.count
Comment(s)
= render #post.comments
= image_tag #comment.image.url(:medium)
I get the error - undefined method `image' for nil:NilClass. Highlighing this row - = image_tag #comment.image.url(:medium)
Any help much appreciated.
undefined method `image' for nil:NilClass
The problem is #comment is nil at this line = image_tag #comment.image.url(:medium) because you haven't declared #comment in the show method of posts_controller.
Solution
As post can have many comments and since #post is available, you can iterate through the #post's comments in the view like below.
- #post.comments.each do |comment|
= image_tag comment.image.url(:medium)
I built nested posts using this closure_tree tutorial:
[Nested Comments with Rails - Sitepoint][1]
Then I installed Devise and began attributing posts to users in the PostsController and Post & User models. How can I add the current_user method to the Create action (the 'if' portion, not the 'else' portion) in my PostsController so I can track the creator of a post which has replies? I have tried several combinations including:
#post = current_user.parent.children.build(post params)
#post = current_user.posts.parent.children.build(post params)
They are all throwing errors.
Here is my code:
Post.rb
class Post < ActiveRecord::Base
belongs_to :user
acts_as_tree order: 'created_at DESC'
end
User.rb
class User < ActiveRecord::Base
has_many :posts, dependent: :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
posts_controller.rb
class PostsController < ApplicationController
before_action :authenticate_user!, except: [:index]
def index
#post = Post.new
#posts = Post.hash_tree
end
def show
#post = Post.find(params[:id])
end
def new
#post = current_user.posts.build(parent_id: params[:parent_id])
end
def create
if params[:post][:parent_id].to_i > 0
parent = Post.find_by_id(params[:post].delete(:parent_id))
#post = parent.children.build(post_params)
else
#post = current_user.posts.build(post_params)
end
if #post.save
flash[:success] = 'Your post was successfully added!'
redirect_to posts_path
else
render 'new'
end
end
def edit
end
def update
end
def destroy
end
private
def post_params
params.require(:post).permit(:content)
end
end
_Post.html.erb partial
<div class="media">
<div class="media-left">
<a href="#">
<img class="media-object" src="http://placehold.it/50x50" alt="...">
</a>
</div>
<div class="media-body">
<%= post.content %></br>
<%= link_to time_ago_in_words(post.created_at), post_path(post) %> ago by <%= post.user.name.capitalize %> | <% from_reply_form ||= nil %>
<% unless from_reply_form %>
<%= link_to 'reply', new_post_path(post.id) %>
<% end %>
</div>
</div>
Thanks!!!
`ThreadedSalesApp::Application.routes.draw do
devise_for :users
resources :users, only: [:show, :index]
resources :posts, only: [:index, :show, :create]
get '/posts/new/(:parent_id)', to: 'posts#new', as: :new_post
root 'posts#index'
end`
I'm having a bit of an issue understanding what is your post nested under? The tutorial you referenced shows how to nest comments. Comments could be nested under posts. I'm sure others will be more helpful than I am given my limited experience but from what I can see, your methods are wrong. Consider the following:
def index
#post = Post.new
#posts = Post.hash_tree
end
In your index, you ought to show an index (think of it as a list) of your objects, in this case it would be the list of Posts. You're initiating a new post which should happen in your 'new' method. In the tutorial you referenced, you'll notice that index is defined as:
def index
#comments = Comment.all
end
To give you an example, if we're nesting posts under Topics, your code could look something like this:
class Topics::PostsController < ApplicationController
def show
#post = Post.find(params[:id])
#topic = Topic.find(params[:topic_id])
end
def new
#topic = Topic.find(params[:topic_id])
#post = Post.new
end
def create
#topic = Topic.find(params[:topic_id])
#post = current_user.posts.build(post_params)
#post.topic = #topic
if #post.save
flash[:notice] = "Your post was successfully added!"
redirect_to [#topic, #post]
else
flash[:error] = "There was an error saving your post."
render :new
end
end
...
Basically, if your posts are nested under something else, you must find that object's ID first to nest it under that. The same could be done with comments (comments being nested under your posts). You would look for the post_id in your Comments Controller.
Figured it out. I needed to merge the user_id: with the post_params, and when calling the user in the post index view I needed to use post.user.name versus user.name.
I am trying to follow the RailsGuides, http://edgeguides.rubyonrails.org/getting_started.html
Now, in section 6.4 where I am supposed to be able to add comments in my blog site, I am encountering this error:
NoMethodError in Posts#show
Showing C:/Sites/blog/app/views/posts/show.html.erb where line #12 raised: </br>
undefined method `comments' for #<Post:0x3a99668
below is the snippet in my show.html.erb
<h2>Add a comment:</h2>
<%= form_for([#post, #post.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
Below is the snippet for my comments controller:
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comment_params)
redirect_to post_path(#post)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
Below is my Posts controller:
class PostsController < ApplicationController
def new
#post = Post.new
end
def create
#post = Post.new(params[:post].permit(:title, :text))
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
end
def index
#posts = Post.all
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(params[:post].permit(:title, :text))
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :text)
end
end
Please help me thanks.
-aguds
It's saying
"line #12 raised:
undefined method `comments' for #< Post:0x3a99668 >"
That means in line 12 of your show.html.erb you are calling comments method on an object of Post, but that method is not defined.
So you need to check in your Post class that following relation is there.
app/models/post.rb
has_many :comments
The server needs to restarted once you add a new Model. (You added Comment.)
The general rule of thumb here is making changes to anything outside of app/ or config/routes.rb will require a restart.
Refer: When do I need to restart server in Rails?
I'm working on an app that allows users to comment on a single "work" (think blog post). The associations in the models are as follows:
class User < ActiveRecord::Base
has_many :works
has_many :comments
class Work < ActiveRecord::Base
belongs_to :user
has_many :comments
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
belongs_to :work
There's a form on the Works show page that allows users to post a comment:
<%= form_for(#comment) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Post a comment!" %>
</div>
<%= f.submit "Post", class: "btn btn-small btn-primary" %>
<% end %>
The Works controller is as follows. Note that I'm adding the build comment functionality here so that the form on the Works page functions:
class WorksController < ApplicationController
#before_filter :current_user, only: [:edit, :update]
def index
#works = Work.all
#comment = #work.comments.build(params[:comment])
#comment.user = current_user
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #works }
end
end
def create
#work = current_user.works.create(params[:work])
redirect_to current_user
end
def edit
#work = current_user.works.find(params[:id])
end
def new
#work = current_user.works.new
end
def destroy
#work = current_user.works.find(params[:id]).destroy
flash[:success] = "Work deleted"
redirect_to current_user
end
def update
#work = current_user.works.find(params[:id])
if #work.update_attributes(params[:work])
flash[:success] = "Profile updated"
redirect_to #work
else
render 'edit'
end
end
def show
#work = Work.find(params[:id])
#comment = #work.comments.build
#comment.user = current_user
#activities = PublicActivity::Activity.order("created_at DESC").where(trackable_type: "Work", trackable_id: #work).all
#comments = #work.comments.order("created_at DESC").where(work_id: #work ).all
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #work }
end
end
end
And lastly, here is the Comments controller:
class CommentsController < ApplicationController
before_filter :authenticate_user!
def index
#comments = Comment.all
end
def show
#comment = Comment.find(params[:id])
#activities = PublicActivity::Activity.order("created_at DESC").where(trackable_type: "Comment", trackable_id: #comment).all
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #comment }
end
def update
#comment = current_user.comments.find(params[:id])
if #comment.update_attributes(params[:comment])
flash[:success] = "Comment updated"
redirect_to #comment
end
end
def create
#work = Work.find(params[:id])
#comment = #work.comments.build(params[:comment])
#comment.user = current_user
if #comment.save
#flash[:success] = "Post created!"
redirect_to #work
else
render 'home#index'
end
end
end
end
When I attempt to submit a comment using the comment form on the works show view page, I get the following error:
Activerecord::RecordNotFound in CommentsController#create
Couldn't find Work without an ID
Why can't the application find the Work so that it can associate the comment to it?
EDIT 1:
Thanks to the answers below I edited the comment form:
<%= form_for(#work, #comment) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Post feedback or contribute content
to this work!" %>
</div>
<%= f.submit "Post", class: "btn btn-small btn-primary" %>
<% end %>
I'm still getting the same error after making the change to the form and adding the nested route.
I edited the routes file to include a nest for work comments:
authenticated :user do
root :to => 'activities#index'
end
root :to => "home#index"
devise_for :users
resources :users do
member do
get :following, :followers, :posts, :comments
end
end
resources :works do
resources :comments
end
resources :relationships, only: [:create, :destroy]
resources :posts
resources :activities
resources :comments
Rake routes shows the following for Comments#create:
POST /comments(.:format)
The POST URL (where the error shows up) is appURL/works/1/comments
Doesn't seem right. What do I need to change? Thank you so much for the help so far!!
Your form needs to be form_for([#work, #comment]) so that Rails knows to build a URL like /works/123/comments. Right now it would just be posting to /comments.
Check your rake routes to see the route for your CommentsController#create action. You might also need to tweak the controller to read params[:work_id] instead of params[:id].
The view helper form_for(#comment) will post to '/comments' by default. You can specify a url (see the guides) that includes the :id of the work record. The typical approach is to use form_for([#work, #comment]) and Rails will do this for you so long as you've set up your routes with comments as a nested resource of work.