1

I generate 3 models: "User", "Article" and "Comment", and the "Comment" model have foreign key "user_id" and "article_id". However I can't automatically add "article_id" when I want to create an comment to a specific article in view.

In rails console, I can add it successfully by using

comment = Comment.new(:content => "Great post")
comment.user = user
comment.article = Article.find(1)
comment.save

I tried to write some code in my controller, but is doesn't work

articles_controller.rb

class ArticlesController < ApplicationController
  before_filter :signed_in_user, only: [:create, :destroy]
  before_filter :correct_user, only: :destroy 

  def index
    @articles = Article.all
  end

  def show
    @article = Article.find(params[:id])
    @comment = Comment.new()
  end

  def create
    @article = current_user.articles.build(params[:article])
    if @article.save
      flash[:success] = "Article created!"
      redirect_to root_url
    else
      render 'static_pages/home'
    end
  end

  def destroy
    @article.destroy
    redirect_to current_user
  end

  private
    def correct_user
      @article = current_user.articles.find_by_id(params[:id])
      redirect_to root_url if @article.nil?
    end
end

comments_controller.rb

class CommentsController < ApplicationController
  before_filter :signed_in_user, only: [:create, :destroy]
  before_filter :correct_user, only: :destroy 

  def create
    @comment = current_user.comments.build(params[:comment])
    if @comment.save
      flash[:success] = "Comment created!"
      redirect_to articles_url
    else
      render 'static_pages/home'
    end
  end

  def destroy
    @comment.destroy
    redirect_to current_user
  end

  private
    def correct_user
      @comment = current_user.comments.find_by_id(params[:id])
      redirect_to root_url if @comment.nil?
    end
end

in view files /articles/show.html file render a partial file "_comment_form.html.erb"

_comment_form.html.erb

<%= form_for(@comment) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="field">
    <%= f.text_area :content, placeholder: "Compose your comment..." %>
  </div>
  <%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>

I can add some new lines to manually add article_id to the comments table. But it is not a good way.

<% @comment.article_id = @article.id %>
<%= form_for(@comment) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <%= f.label :article_id %>
  <%= f.text_field :article_id %>
  <div class="field">
    <%= f.text_area :content, placeholder: "Compose your comment..." %>
  </div>
  <%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>

How can I do? Thank you so much.


changed _comment_form.html.erb

# views/comments/_comment_form.html.erb

<%= form_for[:articles, @comment] do |f| %>
   <%= render 'shared/error_messages', object: f.object %>
   <div class="field">
     <%= f.text_area :content, placeholder: "Compose your comment..." %>
   </div>
   <%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>

The error:

SyntaxError in Articles#show

Showing F:/RailsProject/project/gamespace/app/views/comments/_comment_form.html.erb where line #2 raised:

F:/RailsProject/project/gamespace/app/views/comments/_comment_form.html.erb:2: syntax error, unexpected keyword_do_block, expecting keyword_end
...orm_for[:articles, @comment] do |f| @output_buffer.safe_conc...
...                               ^
F:/RailsProject/project/gamespace/app/views/comments/_comment_form.html.erb:9: syntax error, unexpected keyword_ensure, expecting $end
Extracted source (around line #2):

1: 
2:     <%= form_for[:articles, @comment] do |f| %>
3:        <%= render 'shared/error_messages', object: f.object %>
4:        <div class="field">
5:           <%= f.text_area :content, placeholder: "Compose your comment..." %>

 # comments_controller.rb
 class CommentsController < ApplicationController
  before_filter :signed_in_user, only: [:create, :destroy]
  before_filter :correct_user, only: :destroy 

  def create
    @comment = Comment.new(comment_params)
    @comment.article_id = params[:id]
    if @comment.save
      flash[:success] = "Comment created!"
      redirect_to articles_url
    else
      render 'static_pages/home'
    end
  end

  def destroy
    @comment.destroy
    redirect_to current_user
  end

  private

    def correct_user
      @comment = current_user.comments.find_by_id(params[:id])
      redirect_to root_url if @comment.nil?
    end

    def comment_params
      params.require(:comment).permit(:content).merge(:user_id => current_user.id, :article_id => params[:id])
    end 
end

Error:

TypeError in CommentsController#create

can't convert Symbol into String
Rails.root: F:/RailsProject/project/gamespace

Application Trace | Framework Trace | Full Trace
app/controllers/comments_controller.rb:33:in `comment_params'
app/controllers/comments_controller.rb:7:in `create'
This error occurred while loading the following files:
   comment
Request

Parameters:

{"utf8"=>"✓",
 "authenticity_token"=>"ybkPs+c2068AZIIqCNJz1epBtS4L1zgT/vU9LL2Fs+E=",
 "comment"=>{"content"=>"sdfadfa"},
 "commit"=>"Post",
 "article_id"=>"10"}

-------------------------------------------------------------------------------


Solution:

# comments_controller.rb
def create
  comment = current_user.comments.build(params[:comment])
  comment.article = Article.find(params[:article_id])
  comment.save
end

use hidden_field method

# views/_comments_form.html.erb
<%= form_for [@article, @comment] do |f| %>  
   <%= render 'shared/error_messages', object: f.object %>
   <%= f.hidden_field :article_id, :value => @article.id %>
   <div class="field">
     <%= f.text_area :content, placeholder: "Compose your comment..." %>
   </div>
   <%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
gurugurusa
  • 45
  • 1
  • 6

2 Answers2

1

I was going to write a long post here, but I actually agree with Monk_code - you'll be better to inject the foreign_key into the new record rather than playing around with any tricks

#app/controllers/comments_controller.rb
def create
    comment = Comment.new(comment_params)
    comment.article_id = params[:id]
    comment.save
end

OR

#app/controllers/comments_controller.rb
def create
    comment = Comment.new(comment_params)
    comment.save
end

private
def comment_params
    params.require(:comment).permit(:content).merge(:user_id => current_user.id, :article_id => params[:id])
end

Associative Data

The problem I can see is that your model will probably look like this:

#app/models/comment.rb
Class Comment > ActiveRecord::Base
    belongs_to :article
    belongs_to :user
end

If this is the case, you have to remember that you're saving data which is dependent on a parent model. As it's a dependency, it will not automatically know the foreign key, meaning you'll have to assign it yourself

If you were saving a new Article, you'll be able to pass accepts_nested_attributes_for or even just create a comment record after_create


Correctly Designing Your Application

My solution is to use the params[:id] you'll likely have in place as a result of using

resources :articles do
    resources :comments
end

This means every comment will have to be created on an article's page, thus allowing you to use the params[:id] variable when you create a new record


Update

class CommentsController < ApplicationController
  before_filter :signed_in_user, only: [:create, :destroy]
  #before_filter :correct_user, only: :destroy 

  def new
       @comment = Comment.new
  end

  def create
    @comment = Comment.new(comment_params)
    if @comment.save
      flash[:success] = "Comment created!"
      redirect_to articles_url
    else
      render 'static_pages/home'
    end
  end

  def destroy
    @comment.destroy
    redirect_to current_user
  end

  private
    def comment_params
      params.require(:comment).permit(:content).merge(:user_id => current_user.id, :article_id => params[:article_id])
    end 
end
Richard Peck
  • 76,116
  • 9
  • 93
  • 147
  • You should use this for your form (considering you're using the nested route I suggested): `<%= form_for[:articles, @comment] do |f| %>` – Richard Peck Dec 01 '13 at 15:15
  • F:/RailsProject/project/gamespace/app/views/comments/_comment_form.html.erb:2: syntax error, unexpected keyword_do_block, expecting keyword_end ...orm_for[:articles, @comment] do |f| @output_buffer.safe_conc... ... ^ F:/RailsProject/project/gamespace/app/views/comments/_comment_form.html.erb:13: syntax error, unexpected keyword_ensure, expecting $end – gurugurusa Dec 01 '13 at 15:27
  • Hmm - can you update your question with your new form code please? – Richard Peck Dec 01 '13 at 15:38
  • No problem! The code looks fine (maybe you could try a space between `<%= form_for [:admin, @comment]`. Are you sure you've defined @comment correctly? – Richard Peck Dec 01 '13 at 16:04
  • OK I made it correct <%= form_for [@article, @comment] do |f| %> – gurugurusa Dec 01 '13 at 16:04
  • Nice! http://stackoverflow.com/questions/2034700/form-for-with-nested-resources I was just going to recommend this – Richard Peck Dec 01 '13 at 16:05
  • I updated my question again with adding my create action in comments_controller – gurugurusa Dec 01 '13 at 16:13
  • Why have you chosen this line: `@comment = current_user.comments.build(params[:comment])`? Convention would be `@comment = Comment.new()`. Do you have any particular reasons as to why you're using your current line? – Richard Peck Dec 01 '13 at 16:14
  • Because only log-in user can write comments. comments table also has a foreign key user_id; I tried to use @comment = Comment.new(), but the both foreign_key were blank when it saved – gurugurusa Dec 01 '13 at 16:19
  • hmmm okay - I would highly recommend using the [`CanCan` gem](https://github.com/ryanb/cancan) to manage authentication ([RailsCast](http://railscasts.com/episodes/192-authorization-with-cancan)). This will give you the ability to only allow logged-in users to create comments! This means you can then merge the user's logged-in ID with the submitted form data using strong params – Richard Peck Dec 01 '13 at 16:22
  • I just updated my answer to demonstrate this -> `params.require(:comment).permit(:content).merge(:user_id => current_user.id, :article_id => params[:id])` – Richard Peck Dec 01 '13 at 16:23
  • Looks like it's a problem with `Rails.root` - have you checked your DB to see if it updated? Perhaps it's a redirect problem – Richard Peck Dec 01 '13 at 16:36
  • Okay, thanks for the reply. In the `comment_params` function, try changing `:article_id => params[:id]` to `:article_id => params[:article_id]` – Richard Peck Dec 01 '13 at 16:41
  • You might want to try my update for you - see if that works better – Richard Peck Dec 01 '13 at 16:44
  • Something else is wrong then - can I see a live link to your app? – Richard Peck Dec 01 '13 at 16:50
  • I don't know how to send my app to you – gurugurusa Dec 01 '13 at 16:52
  • Do you have it running on Heroku or another accessible system? – Richard Peck Dec 01 '13 at 16:53
  • Ohh! https://devcenter.heroku.com/articles/getting-started-with-rails4#deploy-your-application-to-heroku that will help – Richard Peck Dec 01 '13 at 16:59
  • I met some problem about Heroku yesterday when I enter "git push heroku master". However the problem on the rails was solved by my lecturer. I added the solution in my question :-) – gurugurusa Dec 02 '13 at 11:45
0

you need something that

comment = Comment.new(:content => "Great post")
comment.user = user
comment.articles << Article.find(1)
comment.save

use << instead =
read this and about has_many

Community
  • 1
  • 1
Roman Kiselenko
  • 43,210
  • 9
  • 91
  • 103