Ask A Question

Notifications

You’re not receiving notifications from this thread.

[ActiveRecord::RecordInvalid - Validation failed: Blog must exist]

Linards Berzins asked in Rails

Hi,

trying to sort out this error for a while now, no luck.

I get this error above when adding a new comment for a blogpost. The blog is clearly there. Any insight would be much appreciated.

_comment.html.erb

<div class="comment-card">
    <div class="card">
        <div class="card-block">
            <div class="row">
                <div class="col-md-1">
                </div>
                <div class="col-md-11">
                    <%= comment.content %>
                </div>
            </div>
        </div>
    </div>
</div>

_comment_form.html.erb

<% unless current_user.is_a? GuestUser %>
  <%= form_for @comment, url: '#' do |f| %>
    <div class="form-group">
      <%= f.label :content %>
      <%= f.text_area :content, class: 'form-control' %>
    </div>
    <%= f.submit 'Post Comment', class: 'btn btn-primary' %>
  <% end %>
<% end %>

comment.rb

class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :blog
  validates :content, presence: true, length: { minimum: 5, maximimum: 1000 }
  after_create_commit { CommentBroadcastJob.perform_later(self) }
end

comment_controller.rb

class CommentsController < ApplicationController
  def create
    @comment = current_user.comments.build(comment_params)
  end
  private
  def comment_params
    params.require(:comment).permit(:content)
  end
end

blogs_controller.rb

  def show
    @blog = Blog.includes(:comments).friendly.find(params[:id])
    @comment = Comment.new
    @page_title = @blog.title
    @seo_keywords = @blog.body
  end

Terminal output:
Could not execute command from ({"command"=>"message", "identifier"=>"{\"channel\":\"BlogsChannel\",\"blog_id\":\"\"}", "data"=>"{\"comment\":\"sdgergre\",\"blog_id\":\"\",\"action\":\"send_comment\"}"}) [ActiveRecord::RecordInvalid - Validation failed: Blog must exist]:

Reply

Thanks Jack,

blog_id is being added actually

blogs_channel.rb

class BlogsChannel < ApplicationCable::Channel
  def subscribed
    stream_from "blogs_#{params['blog_id']}_channel"
  end
  def unsubscribed
  end
  def send_comment(data)
    current_user.comments.create!(content: data['comment'], blog_id: data['blog_id'])
  end
end

blogs_controller.rb

def show
   @blog = Blog.includes(:comments).friendly.find(params[:id])
   @comment = Comment.new
end

show.html.erb

<div class="col-sm-8 blog-main">
    <h2> <%= @blog.title %></h2>
    <%= link_to 'Edit', edit_blog_path(@blog) if logged_in?(:site_admin) %>
    <p><%= @blog.body %></p>

    <%= render 'comments/comment_form' %>

    <div id="comments" data-blog-id="<%= @blog_id %>">
        <%= render @blog.comments %>
    </div>
</div>

I can create the comment in terminal:

[2] pry(main)> Comment.create!(user_id: User.last.id, blog_id: Blog.last.id, content: "1234567")
  User Load (0.4ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1  [["LIMIT", 1]]
  Blog Load (0.2ms)  SELECT  "blogs".* FROM "blogs" ORDER BY "blogs"."id" DESC LIMIT $1  [["LIMIT", 1]]
   (0.1ms)  BEGIN
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Blog Load (0.1ms)  SELECT  "blogs".* FROM "blogs" WHERE "blogs"."id" = $1 LIMIT $2  [["id", 20], ["LIMIT", 1]]
  SQL (54.8ms)  INSERT INTO "comments" ("content", "user_id", "blog_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["content", "1234567"], ["user_id", 1], ["blog_id", 20], ["created_at", "2018-02-02 05:45:37.058157"], ["updated_at", "2018-02-02 05:45:37.058157"]]
   (9.1ms)  COMMIT
Enqueued CommentBroadcastJob (Job ID: 22604b93-a864-4213-a348-0e0da6c69ee0) to Async(default) with arguments: #<GlobalID:0x007f93f1e3cfb0 @uri=#<URI::GID gid://web-portfolio/Comment/7>>
=> #<Comment:0x007f93f52996f0
 id: 7,
 content: "1234567",
 user_id: 1,
 blog_id: 20,
 created_at: Fri, 02 Feb 2018 05:45:37 UTC +00:00,
 updated_at: Fri, 02 Feb 2018 05:45:37 UTC +00:00>
[3] pry(main)>   Comment Load (0.4ms)  SELECT  "comments".* FROM "comments" WHERE "comments"."id" = $1 LIMIT $2  [["id", 7], ["LIMIT", 1]]
Performing CommentBroadcastJob (Job ID: 22604b93-a864-4213-a348-0e0da6c69ee0) from Async(default) with arguments: #<GlobalID:0x007f93f0fd68b8 @uri=#<URI::GID gid://web-portfolio/Comment/7>>
  Blog Load (0.3ms)  SELECT  "blogs".* FROM "blogs" WHERE "blogs"."id" = $1 LIMIT $2  [["id", 20], ["LIMIT", 1]]
  Rendered comments/_comment.html.erb (1.7ms)
[ActionCable] Broadcasting to blogs_20_channel: {:comment=>"\t<div class=\"comment-card\">\n\t\t<div class=\"card\">\n\t\t\t<div class=\"card-block\">\n\t\t\t\t<div class=\"row\">\n\t\t\t\t\t<div class=\"col-md-1\">\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div class=\"col-md-11\">\n\t\t\t\t\t\t1234567\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>"}
Performed CommentBroadcastJob (Job ID: 22604b93-a864-4213-a348-0e0da6c69ee0) from Async(default) in 424.54ms
Reply

Hi Linards,

I think what Jack is referring to is your comments controller create action:

class CommentsController < ApplicationController
  def create
    @comment = current_user.comments.build(comment_params)
  end
  private
  def comment_params
    params.require(:comment).permit(:content) #you're only allowing :content
  end
end

Your comment params are only permitting :content and in your new comment form you don't pass the blog ID either:

<% unless current_user.is_a? GuestUser %>
  <%= form_for @comment, url: '#' do |f| %>
    <div class="form-group">
      <%= f.label :content %>
      <%= f.text_area :content, class: 'form-control' %>
    </div>
    <%= f.submit 'Post Comment', class: 'btn btn-primary' %>
  <% end %>
<% end %>

And in your show action you're just creating a new comment without associating the @blog.id:

def show
   @blog = Blog.includes(:comments).friendly.find(params[:id])
   @comment = Comment.new # you should be building the comment from the blog
end

So what you should be able to do is something like:

def show
   @blog = Blog.includes(:comments).friendly.find(params[:id])
   @comment = @blog.comments.build
end

Which should now properly pass the :blog_id. You may have to play with this some, I haven't tested and I've only had one cup of coffee so my brains not firing on all cylinders yet but this should get you going in the right direction.

You could also do something like this (but the above is the more railsy way)

<% unless current_user.is_a? GuestUser %>
  <%= form_for @comment, url: '#' do |f| %>
    <div class="form-group">
      <%= f.label :content %>
      <%= f.text_area :content, class: 'form-control' %>
      <%= f.hidden_field :blog_id, @blog.id %>
    </div>
    <%= f.submit 'Post Comment', class: 'btn btn-primary' %>
  <% end %>
<% end %>

And then just add to your permitted params:

class CommentsController < ApplicationController
  def create
    @comment = current_user.comments.build(comment_params)
  end
  private
  def comment_params
    params.require(:comment).permit(:content, :blog_id)
  end
end
Reply

Thank you Jacob,

its been sorted now.

Much appreciated!

Reply

That would be fantastic. Thanks!

Reply

The ActiveRecord::RecordInvalid error usually occurs when there is a data validation error during storage into the database using ActiveRecord in Ruby on Rails. To fix this error, you need to recheck your code to identify the error and fix it.

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 87,563+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.