I'm learning rails and trying out polymorphic association. I have listed below a couple of simple models for illustration. Model associations seems to works fine as expected. But what if a user (commenter) would like to leave a comment for a another user? I can't seems to get it to work with these configuration. How do I go about doing so?

class User < ApplicationRecord
  #  username, email, password_digest
  has_many :comments, as: :commentable, dependent: :destroy

class Project < ApplicationRecord
  # title, completed, user_id
  has_many :comments, as: :commentable, dependent: :destroy

class Comment < ApplicationRecord
  # commenter_id, commentable_type, commentable_id, body
  belongs_to :commentable, polymorphic: true

in console... setup

user1 = User.frist
user2 = User.last
project = Project.first

pro_comment = project.comments.new(body: 'some text')
pro_comment.commenter_id = user1.id

user_comment = user2.comments.new(body: 'some text')
user_comment.commenter_id = user1.id

expected and actual results

Comment.all => successfully lists pro_comment & user_comment

Comment.find_by(commenter_id: 1) => only listed the pro_comment 
(what am I doing wrong?)

Also.. user1.comments => returned an empty object... was expecting 2 objects, as you can see below it's not referencing 'commenter_id' .... result...

comment Load (0.5ms)  SELECT  "comments".* FROM "comments" WHERE 
"comments"."commentable_id" = $1 AND "comments"."commentable_type" = $2 
LIMIT $3  [["commentable_id", 1], ["commentable_type", "User"], 
["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy []>

I also tried ... user1.comments.where(commenter_id: 1) >> which returned...

comment Load (0.4ms)  SELECT  "comments".* FROM "comments" WHERE
 "comments"."commentable_id" = $1 AND "comments"."commentable_type" = $2 
AND "comments"."commenter_id" = $3 LIMIT $4  [["commentable_id", 1],
["commentable_type", "User"], ["commenter_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::AssociationRelation []>

Not sure what I'm doing wrong. Could someone please point me in the right direction. I thank you for your time.

1 Answers

arieljuod On Best Solutions

find_by returns only one record, try Comment.where(commenter_id: 1) instead.

For user1.comments being empty, you are mixing the relationships. You should have 2 relationships: comment belongs to a commentable object (a project or a user) and comments also belongs to a commenter (the user you set as commenter_id).

It makes sense for user1.comments to be empty since the user is the commenter on both comments, it's not the commentable. user2.comments shouldn't be empty, same for project.comments

Try something like this:

class User < ApplicationRecord
  has_many :comments_done, class_name: 'Comment', inverse_of: :commenter
  has_many :comments, as: :commentable, dependent: :destroy

class Comment < ApplicationRecord
  belongs_to :commenter, class_name: 'User'
  belongs_to :commentable, polymorphic: true

(check the guide, I may be missing some config option https://guides.rubyonrails.org/v5.2/association_basics.html#has-many-association-reference)

Now you can use user1.comments_done and user1.comments for comments done by the user and done at the user.