Search code examples
ruby-on-railsdatabasemodel-view-controllerdevisemigration

can't write unknown attribute `user_id`


I am using gem devise for creating user profiles. Every user can create comments in any profile I want to add each user name under the comment he wrote.

A user has many comments

A user has many profile comments, this is the relationship between a user and the comments This is the error that I got when I create a comment

can't write unknown attribute `user_id`

Here is what I did

class CreateProfileComments < ActiveRecord::Migration
      def change
        create_table :profile_comments do |t|
    
          t.timestamps null: false
        end
      end
    end

class CreateComments < ActiveRecord::Migration
  def change
    create_table :comments do |t|
      t.text :text
      t.references :user, index: true, foreign_key: true
      
      t.timestamps null: false
    end
  end
end

in user.rb model

  has_many :comments
  has_many :profile_comments, dependent: :destroy

in comment.rb

class Comment < ActiveRecord::Base
  belongs_to :user #not users as you have defined in your question
  has_many :profile_comments
end

in profile comment.rb

class ProfileComment < ActiveRecord::Base
  belongs_to :comment
  belongs_to :user
end

in comment controller

class CommentsController < ApplicationController
  before_action :find_comment ,only:[:show,:update,:edit,:destroy]

   def new
    @user =User.find(params[:id])
    @comment = @user.comments.build
  end

  def index

  end

  def create
    @user =User.find(params[:id])
    @comment = current_user.comments.build(comment_params)
    @profile_comment = ProfileComment.new
    @user.profile_comments < @profile_comment
    @comment.profile_comment < @profile_comment
    if @comment.save
      redirect_to doctor_path(:id => @user.id)
    end
  end 

  def edit
  end

  def update

    if @comment.update(comment_params)
      redirect_to root_path, alert: "user Information updated successfully"
    else
      flash.now[:error] = "Couldn't update!"
      render :edit
    end
  end
  def destroy
    @comment.destroy
    redirect_to doctors_path, notice: "comment deleted Successfully"
  end 

private

  def find_comment
    @comment = Comment.find(params[:id])
  end

  def comment_params
    params.require(:comment).permit(:text)
  end
end

in user profile view

      <% @user.profile_comments.each do | profile_comment |%>
  <%comment = profile_comment.comment%>
  <% if comment.text.present? %>
    <%= comment.text %><br>
    <%if comment.user.blank?%>
      No user assigned to this comment
    <%else%>
      <%= comment.user.name #or email or whatever%>
    <%end%>
    <br><hr>
  <% end %>
<% end %>

Solution

  • I'm guessing what you actually want is just to create two assocations pointing to the same table:

    class CreateComments < ActiveRecord::Migration
      def change
        create_table :comments do |t|
          t.text :text
          t.references :user, index: true, foreign_key: true
          t.references :author, index: true, foreign_key: { to_table: :users }
          t.timestamps null: false
        end
      end
    end
    

    Here user_id references the target user of the comment and author_id references the user writing the comment. Both reference users.id.

    Then create two belongs_to associations in your comment model:

    class Comment < ApplicationRecord
      belongs_to :user
      belongs_to :author, 
        class_name: 'User', 
        inverse_of: :authored_comments
    end
    

    And two has_many associations in your User model:

    class User < ApplicationRecord
      # comments about this user
      has_many :comments 
      # comments written by this user 
      has_many :authored_comments, 
        class_name: 'Comment',
        foreign_key: :author_id,
        inverse_of: :author
    end
    

    I have no idea why you would want to mix in another table as both relations are one to many.

    class CommentsController < ApplicationController
      before_action :set_user, only: [:new, :create]
      
      # ...
    
      def new
        @comment = @user.comments.new
      end
    
      def create
        @comment = @user.comments.new(comment_params) do |c|
          c.author = current_user
        end
        if @comment.save
          redirect_to doctor_path(id: @user.id)
        else
          render :new
        end
      end 
    
      private
    
      def set_user
        @user = User.find(params[:id])
      end
    
      # ...
    end
    

    If you then want to display comments you would do:

    <% @user.comments.each do |comment| %>
    <div class="comment">
      <div class="body">
        <%= comment.body %>
      </div>
      <p class="author"><%= comment.author.email %></p>
    </div>  
    <% end %>