0

When attempting to create a user comment I receive the following errors. I then plan to edit and delete the comments once this is working.

ActiveRecord::RecordInvalid in CommentsController#create

Validation failed: User must exist

comments_controller

class CommentsController < ApplicationController
  before_action :set_post

  http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]

  def create
    @comment = @article.comments.create!(comment_params)
    @comment.user = current_user
    redirect_to article_path(@article), notice: "Comment added successfully"
  end

  def destroy
    @comment = @article.comments.find(params[:id])
    @comment.destroy
    redirect_to article_path(@article), status: :see_other, notice: "Comment deleted successfully"
  end

  private

  def set_post
    @article = Article.find(params[:article_id])
  end

  def comment_params
    params.require(:comment).permit(:body, :status)
  end
end

comment.rb

class Comment < ApplicationRecord
  # include Visible

  belongs_to :article
  belongs_to :user
  has_rich_text :body
end

article.rb

class Article < ApplicationRecord
  include Visible
  has_many :comments, dependent: :destroy
  belongs_to :user   

  validates :title, presence: true, length: {minimum: 1, maximum: 40}
  validates :body, presence: true, length: { minimum: 10, maximum: 10000 }
end

user.rb

class User < ApplicationRecord

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :confirmable

  has_many :articles   
  has_many :comments   
  
  def create
    # Create the user form params
    @user = User.new(user_params)
    if @user.save
      # Send the email
      UserNotifierMailer.send_signup_email(@user).deliver
      redirect_to(@user, :notice => 'User created')
    else
      render :action => 'new'
    end
  end

  private

  def user_params
    params.require(:user).permit(:name, :email, :login)
  end

  # set user privileges within console using:
  # @user = User.first
  # @user.role = 1 # moderator
  # @user.save
  enum role: [:user, :moderator, :admin]
  after_initialize :set_default_role, :if => :new_record?

  def set_default_role
    self.role ||= :user
  end

end

schema

ActiveRecord::Schema[7.0].define(version: 2023_05_19_130034) do
  create_table "action_text_rich_texts", force: :cascade do |t|
    t.string "name", null: false
    t.text "body"
    t.string "record_type", null: false
    t.bigint "record_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true
  end

  create_table "active_storage_attachments", force: :cascade do |t|
    t.string "name", null: false
    t.string "record_type", null: false
    t.bigint "record_id", null: false
    t.bigint "blob_id", null: false
    t.datetime "created_at", null: false
    t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
    t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
  end

  create_table "active_storage_blobs", force: :cascade do |t|
    t.string "key", null: false
    t.string "filename", null: false
    t.string "content_type"
    t.text "metadata"
    t.string "service_name", null: false
    t.bigint "byte_size", null: false
    t.string "checksum"
    t.datetime "created_at", null: false
    t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
  end

  create_table "active_storage_variant_records", force: :cascade do |t|
    t.bigint "blob_id", null: false
    t.string "variation_digest", null: false
    t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
  end

  create_table "articles", force: :cascade do |t|
    t.string "title"
    t.text "body"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "status"
    t.integer "user_id", null: false
    t.integer "views", default: 0
    t.index ["user_id"], name: "index_articles_on_user_id"
  end

  create_table "comments", force: :cascade do |t|
    t.string "commenter"
    t.text "body"
    t.integer "article_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "status"
    t.integer "user_id", null: false
    t.index ["article_id"], name: "index_comments_on_article_id"
    t.index ["user_id"], name: "index_comments_on_user_id"
  end

  create_table "users", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.string "confirmation_token"
    t.datetime "confirmed_at"
    t.datetime "confirmation_sent_at"
    t.string "unconfirmed_email"
    t.integer "role", default: 0
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "username"
    t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
  end

  add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
  add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
  add_foreign_key "articles", "users"
  add_foreign_key "comments", "articles"
  add_foreign_key "comments", "users"
end

routes

Rails.application.routes.draw do
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
  root "articles#home"

  devise_for :users, controllers: {
    sessions: 'users/sessions',
    registrations: 'users/registrations'
  }

  # allows for chained url routes
  # eg. /posts/1/comments/4
  resources :articles do
    resources :comments
  end
end

comments/edit.rb

<h1>Edit comment</h1>

<%= render 'comments/form', comment: @comment %>

comments/_form.html.erb

<%= form_with model: [@article, @article.comments.build] do |form| %>

<div class="input-group mb-3 w-50">
  <span class="input-group-text"><%= form.label :body %>
  </span>
  <%= form.text_area :body, :class => "form-control", :rows => 8 %>
</div>

<div class="input-group mb-3 w-25">
  <span class="input-group-text"><%= form.label :status %>
  </span>
  <%= form.select :status, options_for_select(['public', 'private', 'archived']), {}, :class => 'form-select' %>
</div>

<%= form.submit :class => "btn btn-primary" %>

<% end %>

comments/_comment.html.erb

<ul class="list-group list-group-flush w-50">
<li class="list-group-item">
  <p>
    <strong>User:</strong>
  <%= comment.user.username%>
</p>

<p>
  <strong>Comment:</strong>
  <%= comment.body%>
</p>

<p class="timestamp">
  <u>Created on:</u> <%= comment.created_at.strftime("%d %b %y %H:%M:%S") %> 
  <u>Updated on:</u> <%= comment.updated_at.strftime("%d %b %y %H:%M:%S") %>
</p>
<%= if current_user == comment.user %>
<p>
  <%= link_to "Edit", edit_article_comment_path(comment.article, comment), :class => "link-primary link-offset-2 link-underline-opacity-0 link-underline-opacity-100-hover me-3" %>
  <%= link_to "Delete", [comment.article, comment], :class => "link-danger link-offset-2 link-underline-opacity-0 link-underline-opacity-100-hover", data: {
    turbo_method: :delete,
    turbo_confirm: "Are you sure?"
  } %>
</p>
<% end %>

</li>
</ul>
<hr>

I have tried following the following but cannot get it to work: Youtube vid, this and this

Luca
  • 97
  • 7

1 Answers1

1

You are trying to create the comment before associating the user, try this instead

 def create
    @comment = @article.comments.build(comment_params)
    @comment.user = current_user
    @comment.save
    redirect_to article_path(@article), notice: "Comment added successfully"
  end