Is there any way to validate that a polymorphic association is only related to one item? for example, if I have a comments that are polymorphic and can be on photos, posts, etc. I want to ensure that if I am adding a comment to a posts' list of comments, that if the comment is already associated with the post, the add will fail. (validation uniqueness error). Any ideas?
So I'm guessing you have something like this:
class Comment < ActiveRecord::Base
belongs to commentable, :polymorphic => true
end
class Post < ActiveRecord::Base
has_many :comments, :as => commentable, :dependent => :destroy
end
class Photo < ActiveRecord::Base
has_many :comments, :as => commentable, :dependent => :destroy
end
Assuming your Comment model has a couple of attributes like author and body (that you want to be unique) then you could create a custom validation in that model like this:
validate do |comment|
if comment.commentable_type.constantize.comments.find_by_author_and_body(comment.author, comment.body)
comment.errors.add_to_base "Duplicate comment added for ..."
end
end
I've also assumed that comments are created something like this:
#post.comments.create(:author => name, :body => comment_text)
Related
According to this answer i'm trying to inplement STI and Polymorphic associations together, my code:
class Post < ActiveRecord::Base
belongs_to :content, :polymorphic => true
end
class Topic < Post #ActiveRecord::Base
has_one :post, :as => :content, :dependent => :destroy
end
class Tutorial < Post #ActiveRecord::Base
has_one :post, :as => :content, :dependent => :destroy
end
In post table i have columns content_id, content_type
and in tables Topic, Tutorial column body
How can i create (in irb) new Tutorial or Topic ?
i tried Post.topics.new(..., :content => {body: 'my_text'})
but get an error
well first of all i can't see STI here as you are inheriting from ActiveRecord in each model and secondly you are creating new topic but your syntax is wrong. Each Topic has one post and each post belongs to a Topic, so you should do something like Topic.post.build(params) to create a post and if you want to create a topic then Post.topic.build(prams)
How can I validate relations in models in RoR? For example I have 3 models:
class Post < ActiveRecord::Base
belongs_to :blog
has_one :user, :through => :blog
validates :blog_id, :presence => true
end
class Blog < ActiveRecord::Base
belongs_to :user
has_many :posts, :dependent => :destroy
end
class User < ActiveRecord::Base
has_many :blogs
has_many :posts, :through => :blogs
end
And in my controller:
#post = current_user.blogs.find(params[:post].delete(:blog_id)).posts.build(params[:post])
But when I want to create post I get:
Can't mass-assign protected attributes: blog_id
I shouldn't get this error, because I am delete blog_id from params hash, or don't? Any way, what the better way of validating blog_id accessory to User.blogs in my Post model?
If you want to set the on which blog the post should be published after writing, you have to put the blog_id into the whitelist by setting attr_accessible
So in your example your Post model should look like
class Post < ActiveRecord::Base
belongs_to :blog
attr_accessible :blog_id, :title, :content
validates :blog_id, :presence => true
end​
Besides this. Be careful how you set up your relation. The difference between has_one and belongs_to is where the foreign key goes. It goes to where you define the belongs_to. has_one says that one of something is yours, so something points back to you. It doesn't make much sense to say that a Post has_one user...
It is enough to have a Post only belong to a Blog. You still can do something like current_user.posts by how you setup the relationship in the user model like you already did...
I'd recommend reading the following links http://guides.rubyonrails.org/association_basics.html
http://api.rubyonrails.org/classes/ActiveModel/MassAssignmentSecurity/ClassMethods.html
Validations for :blog_id in Post model is perfect.
#post = current_user.blogs.find(params[:post].delete(:blog_id)).posts.build(params[:post])
:blog_id is deleted from params[:post], but let's look at the thing from a different view.
#blog = current_user.blogs.find(params[:post].delete(:blog_id))
#post = #blog.build(params[:post])
params[:post] does not have :blog_id, but build method automatically assigns blog_id to #blog.id.
That's why error for :blog_id is not coming up.
If you wanna avoid the mass assignment warning, you can make the :blog_id attribute accessible.
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.
I am working in a rails app which main points are articles and products.
Someone has implemented categories for articles.
class ArticleCategory < MainSchemaBase
belongs_to :user
has_many :articles, :through => :article_category_articles, :conditions => 'articles.deleted_at IS NULL'
has_many :article_category_articles, :conditions => 'article_category_articles.deleted_at IS NULL'
And I have been asked to do basically the same thing for products.
Of course I want to DRY but products belongs to brand instead of user, and I have many products instead of many articles
The model is almost empty (some named scopes), controller and views also very dependent of the context (article)
Can this be DRY? Or should I just copy the implemntation ?
Make it polymorphic: The best way, i think, is to set up a polymorphic many-to-many relationship using has_many_polymorphs (https://github.com/Nielsomat/has_many_polymorphs) so that a single category could be applied to a product and an article.
class Category
#doesn't have any association fields
has_many_polymorphs :categorizables, :from => [:products, :articles], :through => :categorizations, :dependent => :destroy
end
class Categorization < ActiveRecord::Base
#has fields categorizable_id, categorizable_type, :category_id
belongs_to :categorizable, :polymorphic => true
belongs_to :category
end
class Product < ActiveRecord::Base
#doesn't need anything to set up the association
end
class Article < ActiveRecord::Base
#doesn't need anything to set up the association
end
"Categorizable" is a bit of a mouthful but you won't actually be using it. You'll be saying #product.categories or #category.articles etc.
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