New Discussion

Notifications

You’re not receiving notifications from this thread.

Comments With Polymorphic Associations Discussion

98
General

Awesome Video. I really liked and refreshed my concepts about Polymorphic Associations. Thanks :)

Here is a Pro episode suggestion - take this and add nested comments and some AJAX to it :)

Id be sure to subscribe for that. Not found a decent example of nested before.

+1 on the suggestion. Daniel's link is helpful, but I'm getting caught up on the polymorphic_url generation to use the _comment partial as a shared file between mutliple models. 
Nick Chernyshev Nick Chernyshev

Polymorphism is the main anti-pattern in rails(

Jonathan Denney Jonathan Denney

How would you suggest getting all the comments a user's posts collectively have? For example @user.comments.all throws the error
ActiveRecord::HasManyThroughAssociationNotFoundError at / Could not find the association :commentable in model User

Is this something to consider in a social networking site? If I would have say comments polymorphically associated with various models, wouldn't it be a major hit on one table all the time?

Since they are reads, I think that so long as your database server can handle it, it doesn't matter how many connections read from the same table. Reads don't lock the table/row like writes do since a read cannot cause data loss. You shouldn't experience any loss of speed if many things are reading the same table.

The problem with polymorphic models and tables is there is no way to keep the database from becoming corrupt due to no FK constraints.

I would suggest that you please either put in minimal security on these screen casts or at least mention that the implementation is very insecure and to check out the rails security guidelines. I know the case can be made where security is out of scope for this discussion but a, at one point you reference how this method is "more secure" when talking about the hidden form field and b, you leave xss/injection etc all wide open so that body may be used to by malicious users to extend reach.

Can you please elaborate on this part `you leave xss/injection etc all wide open so that body may be used to by malicious users to extend reach`? I'd like to understand the security risk a little more.

How would this be handled if we only want the user to see their own comments, but not anyone else's?

You could do that by scoping the comments further by adding ".where(user: current_user)" to the query in the controller.

you can scope it like this (if comment.user_id == current_user.id) {...}

I found some of this information useful on how to test these polymorphic comment features at https://github.com/thoughtb...

Interesting way to do this. I created an app that does comments, but I did it by the whole post has_many :comments, comments belongs_to post, and resource nesting. Took me hours to figure out how to do display comments. What's the pros and cons of the that way versus this way?

One great thing about this episode is that the whole polymorphic thing finally clicks for me, because for the longest time I still didn't really understand it despite reading on it repeatedly. In all of my albiet smallish projects I never used it, now that I understand it finally, I have some idea when to use polymorphic association.

Thanks so much! I signed up for GoRails based on this episode and the omniauth twitter episode!

The main difference is that you probably have tied your comments to the Post model. With polymorphism, you could have comments on the Post model, the User model, or any other model you've got.

And thank you a bunch for subscribing!!

Michael Langat Michael Langat

How do you add a destroy method to this

I am trying to add a delete comment button to this. But it is sending me to the articles controller destroy action. Please can you add how to add a delete button to the comment? I'm stuck. Thanks very much!

Hey Melanie!

For deleting comments, it might be useful to add a "resources :comments" to your routes that isn't inside another resources block. Then you can create a regular CommentsController with a destroy action like normal. That way you can delete any comment as long as you know the ID of it and not worry about whether it's a film or actor comment because that doesn't really matter when you're deleting a comment.

To add the delete link to each comment in the view, you can say: <%= link_to "Delete", comment, method: :delete %> and that will make a DELETE request to the /comments/1 url, which will trigger the destroy action.

That should do it! If you want to go over and above, you can also make it as a "remote: true" link so that you can return some JS to remove the item from the page to make it AJAXy and nicer to use.

Hi Chris, I'm still struggling along in trying to get this set up. My current issue is with my comments policy update function. Your tutorial shows how to define update as set out below (which I have tried in both my article policy and my comment policy:

Article Policy:

def update?

#user && user.article.exists?(article.id) -- I have also tried this on this suggestion of someone on stack overflow).

user.present? && user == article.user

end

Comment Policy:

def update?

user.present? && user == comment.user

end

I keep getting a controller action error which says: undefined method `user' for #<class:0x007f9e24fa7cf0>

It highlights the update definition. Has something changed in pundit that requires a different form of expression for this update function? Can you see what might have gone wrong? Thanks very much

That looks correct. It sounds like the comment object is potentially the class and not the instance of the comment.

Does your controller have " authorize @comment" in it? And is your @comment variable set to an individual record?

Chris, Fantastic episode here. Thank you very much.

I got it to work - mostly - in my set-up but I encounter a little redirect problem.

In my case my association is a "noteable" - referring to Notes.

My resources and namespace profile is:

namespace :navigate do
--resources :boks, :only => [:show] do
----resources :tools, :only => [:show, :index]
------resources :notes, module: :processus
----resources :processus do
------resources :notes, module: :processus

The problem I have is with the NotesController for the create/update of the notes. When I try to do a redirect I am finding that I am missing the information about the "bok" entity.

In my controller I have this:

def create
@note = @noteable.notes.new note_params
@note.user = current_user
@note.save
redirect_to [:navigate, @bok, @noteable], notice: "Your note was succesfully created."
end

Really the redirect_to should send me back to the right place, however the @bok entity is not present at all...mostly because at this stage I don't actually need it.

What would be the recommended approach to dealing with this nested situation?

Thanks!

Chris, awesome episode! I'm trying to implement something similar, but before I dive deep into it I'd like to make sure I go down the right path. So I will do the exactly same, except comments are gonna have replies. My guess is if I have the right polymorphic setup for the comments, then I can just setup a simple `has_many + belongs_to` relationship between the comment model and the reply model, so from the reply's perspective it doesn't matter if the comment is polymorphic or not since every comment will have its unique id. Is this right?

Yeah, you can have a comment as a commentable, allowing you to have Comments with comments if you like. That's how you would normally setup threaded comments. You might want to put some limits on the nesting so you don't get threads that are too far nested in. Facebook limits it to one layer for example.

In the comments controller when I try to do

@comment.user = current_user

Throws an error (no user method for user class) unless I do

@comment.user_id = current_user.id

Problem is, when I try to show the username associated to a comment (I need to do queries instead of accessing the object). Any idea on how to fix this?

Oops, I never did the belongs_to :user association :$

Question Chris - I'm trying to show the last 5 comments in the show view. I can't get the query string right. Thanks for your help!

You could change it to the following:

<% commentable.comments.order(created_at: :desc).limit(5).each do |comment| %>

Daniela Correa Orozco Daniela Correa Orozco

Hi Chris!
Awesome video!
I'm having problems with current_user being nil in the base CommentsController where we set
@comment.user_id = current_user

current_user seems to return the right user_id in my other controllers. I have looked around but I haven't been able to pin point the problem (cookie problems maybe?)
Thank you in advance and for the really helpful videos!

Is the user not currently signed in by chance when you submit a comment?

I love this nifty bit of code. I'm wondering how would you go about using will_paginate to paginate the comments?

I forgot about this. I watched it a year ago and used in in my last attempt at implementing these associations. And then I forgot about it. Im back on track again - thanks for this (again). I'm struggling to figure out how to filter the index of the comments resource by an attribute (say :status == 'published'). I can't do that in the regular way because the route is looking for a prefix (of film/actor)

Hey Melanie! :)

If you wanted a route to get all published comments (not ones scoped to a commentable type) you could add a resources :comments that was not nested in your routes and use that.

  resources :comments

resources :actors do
resources :comments, module: :actors
end
resources :films do
resource :comments, module: :films
end

And then you could make a comments_controller.rb that worked for all comments for any object. Is that what you're looking for?

Jessica Ellias Jessica Ellias

Thank you Chris!

Just coming back here to say thanks! I watched this several times and it eventually sunk in. I managed to set it up a few months ago and it's been working quite well. I will admit it did take me some time to grasp the concept though.

That's great to hear! :D And I agree, it's a tough one to wrap your head around the first time.

Khemlall Mangal Khemlall Mangal

Hi all, i am getting issue with my routes when i follow this methodology. I get uninitialized constant Squeals.
SQUEAL Models

class Squeal < ActiveRecord::Base
has_many :comments, as: :commentable
end
comment.rb

class Comment < ActiveRecord::Base
belongs_to:commentable, polymorphic:true
end
/squeal/comments_controller.rb

class Squeals::CommentsController <commentscontroller before_action="" :set_commentable="" private="" def="" set_commentable="" @commentable="Squeal.find(params[:squeal_id])" end="" end="" comments="" controller="" class="" commentscontroller="" <="" applicationcontroller="" before_action:authenticate_user!="" def="" create="" @comment="@commentable.comments.new" comment_params="" @user.user="current_user" comment.save="" redirect_to="" @commentable,="" notice:="" "your="" comment="" was="" posted"="" end="" private="" def="" comment_params="" params.require(:comment).permit(:body)="" end="" end="" routes="" resources="" :squeals="" do="" resources="" :comments,="" module:="" :squeals="" end="">

Hi Chris, I want to add comments to multiple films at the same time. Suppose I want to add comments section on index page of films. Can you please help me how can i do that.

Dinesh Pallapa Dinesh Pallapa

thanks,It's really help full.If we want to delete the comment from actor or film we need to define a method as destroy or else using _destroy for nested attributes.Can anyone help me out.

@excid3:disqus
How would you need to modify this to work with deeply nested resources?

i.e:



resources :projects do
resources :project_users, path: :users, module: :projects
resources :posts do
resources :comments, module: :posts
end
end

I am trying to use commentable with actioncable , but I keep getting the following error
d6f951d17) from Async(default) in 20.73ms: ActionView::Template::Error (undefined method `comments' for #<class:0x007f5cec08b880>):
someone advice me to look for value of comments and I realized is not define
but my question is Is it necessary to define relationship between comment and lets say article class ?

class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true #, optional: true
belongs_to :article, optional: true <--- this point

validates :body, presence: true, length: {minimum: 5, maximimum: 1000 }
after_create_commit {CommentBroadcastJob.perform_later self}
end

Being new at programming and Rails this is the first time I land on the polymorphic association concept and got It completely. I find It super useful and You made It "easy" to understand and implement. Thanks!

Hi Chris! Can this form be used on an index page? I'm noticing that I only get it to work on a show page. Any help

It would great if you could expand this to include comment replies. There are no great tutorials on how to accomplish this task.

Been planning on doing that soon. Thinking about doing this in a series where we create an embeddable Javascript comment system like Disqus.

Do it!

I'd love to see this as well. Tried briefly and was unsuccessful.

That would be awesome. Thanks!

How we delete these polymorphic comments while we can't type the actor_comment_path or film_comment_path ? i used case statement, any smarter idea ?

How can I get all of the users that have commented? What I would like is to be able to:

@post.commenters.each do |u|
something here...
end

To make it as commenters you'd need to set an alias on the users association on comments. Hope this helps. Sorry I don't have exact code for it.

@post.comments.users would work if you had your comments has_many: users association on your comment model.

I have belongs_to :user in my Comment model

belongs_to :commentable, polymorphic: true
belongs_to :user

Got it! In my comments.rb:

has_many :commenters, through: :comments, source: :user

If you use:

rails g model Comment commentable:references{polymorphic}

rails auto creates the compound index for lookup as part of the migration:

      t.references :commentable, polymorphic: true

This works in Rails 5 and probably goes back to rails 4.

Join the discussion
Create an account Log in

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

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

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