I have a Comments model, and I also have a Video, and Photo model. Now, I want for my Video and Photo models to have_many comments, but that means my Comment model will have to have a belongs to :video and a belongs_to :model (as well as foreign keys for each model in the database). Now say I create a Post model in that same application and I want it to have many comments, that would mean I would have to add belongs_to :post to my Comment class. In rails is there a better way to implement a Comment model when there are many other models that are going to have an association with it, or is this just how it is done? Any advice would be much appreciated.
You're looking for polymorphic associations.
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
class Photo < ActiveRecord::Base
has_many :comments, :as => :commentable
end
class Video < ActiveRecord::Base
has_many :comments, :as => :commentable
end
You also have to make some changes to your migrations, see the linked documentation for more information.
Related
I'm trying to implement a polymorphic association in a project for the first time but I'm not a fan of how the associations read and was wondering if there's a way of aliasing them?
Example:
# app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
# app/models/post.rb
class Post < ActiveRecord::Base
has_many :comments, :as => :commentable
end
# app/models/picture.rb
class Picture < ActiveRecord::Base
has_many :comments, :as => :commentable
end
Let's say I wanted to retrieve a Post instance from a given Comment, terrific_post = Comment.first.commentable just doesn't read well in my opinion. Is there a way to alias different association names in the Comment model and avoid relying on a single name such as commentable? I realize you could choose a name which aligns better to your specific dsl rather than say "commentable", however I'd prefer to continue referring to associations with names (or variations) based on their relationships, similar to Comment.first.post and `Comment.first.picture' if at all possible.
At the end of the day it's not a big sacrifice for the flexibility you get with polymorphic associations. Just curious if a solution exists.
Note: the following example was taken from The Odin Project which does a great job explaining various types of associations.
You can alias an association like any other method:
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
alias_method :post, :commentable
alias_method :picture, :commentable
end
And you can then do Comment.first.post and Comment.first.picture.
But Comment.first.post can be either a Post or a Picture,
so you should know what are you doing.
Another approach would be implement methods that return post only if commentable is a Post and picture only when commentable is a Picture:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
def post
commentable if commentable.is_a? Post
end
def picture
commentable if commentable.is_a? Picture
end
end
I've ended up packaging my solution into a gem for experimental purposes. Feel free to try it.
My scenario is that there are several different models which can have comments. Trying to figure out the relationships:
Post
has_many :comments
Update
has_many :comments
Comment
belongs_to EITHER :post OR :update (but not both)????
Whats the proper way to set up the comment relationships? I want to be able to call Post.comments and Update.comments
Smells like a polymorphic association:
With polymorphic associations, a model can belong to more than one other model, on a single association. For example, you might have a picture model that belongs to either an employee model or a product model.
So you'd want something like this:
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
class Post < ActiveRecord::Base
has_many :comments, :as => :commentable
end
class Update < ActiveRecord::Base
has_many :comments, :as => :commentable
end
You'd have to set up a few things in the database for this to work as well. See the Polymorphic Associations section of the Active Record Associations Guide for details on the columns you'll need.
Hey guys,
I'm new to rails
here are the 2 ways of making a Drummer model and Cymbal model both have many videos
1st way by using polymorphic:
class Drummer < ActiveRecord::Base
has_many :videos, :as => :videoable
end
class Cymbal < ActiveRecord::Base
has_many :videos, :as => :videoable
end
class Video < ActiveRecord::Base
belongs_to :videoable, :polymorphic => true
end
2nd way by using two 1:m association:
class Drummer < ActiveRecord::Base
has_many :videos
end
class Cymbal < ActiveRecord::Base
has_many :videos,
end
class Video < ActiveRecord::Base
belongs_to :drummer
belongs_to :cymbal
end
I haven't try them in console, But I think both will work as they should. But I don't know the difference?
I believe that you must use polymorphic method because a model cannot belongs_to (one to one association) more than one other model. For more info see this rails guide: http://guides.rubyonrails.org/association_basics.html
It depends on the columns you have in your database. If you have videoable_type and videoable_id you're doing polymorphism. In that case, calling videoable on an instance of Video can return anything, it's not related to drummers or cymbals. If it is drummer_id and cymbal_id it's the latter version you described.
Both will work.
Imagine you have 5 models sharing Videos.
You would need 5 modelName_id columns in videos.
To determine which type of parent model you have on Video you would need to check each one for id presence.
Validating that only one of the ids is set would be necessary (or valuable).
Polymorphic relations are easier to maintain and expand in such cases.
My needs are very simple: I have a Tip table to receive comments and have comments to receive comments, too.
To retrieve each comment that is stored in the same table (comments), I created another key for the comments on comments: "inverse_comments".
I tried to use one comments table by using self-referntial association. Some resources seem to bring more than one table into the piture which are diffent from my needs. So I came up whth the following modeling for comments:
class Comment < ActiveRecord::Base
belongs_to :tip
belongs_to :user
has_many :mycomments,
:through => :inverse_comments,
:source => :comment
end
Apparently something is missing here but I cannot figure it out.
Could some one enlighten me on this:
what changes I need to do to make the model work?
thanks.
I believe you should use a polymorphic association.
For that you'll need to add a commentable_id and a commentable_type on your comments table. And your models should look like:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :commentable, :polymorphic => true
has_many :comments, :as => :commentable
end
class Tip < ActiveRecord::Base
has_many :comments, :as => :commentable
end
This way you can use
#tip.comments
#comment.comments
Say I'm writing a blog app with models for posts, pages and photos. I've got a category model, that may be linked to any of these models. So, a category may contain various kinds of items. Every item only has ONE category.
I could implement this using a generic tagging pattern with a join table, but I want to make sure every subject can have only category.
What would be the best way to implement this in Rails?
Okay, I think I've got it:
class Post < ActiveRecord::Base
has_one :categorization, :as => :categorizable
has_one :category, :through => :categorization
end
class Category < ActiveRecord::Base
has_many :categorizations, :dependent => :destroy
end
class Categorization < ActiveRecord::Base
belongs_to :category
belongs_to :categorizable, :polymorphic => true
end
Now various models can have a category, but each instance can have only one category… I guess.