I have these models: Post, TextPost and PhotoPost and I use polymorphic association.
You can find these three models here or look below.
post.rb
class Post < ActiveRecord::Base
belongs_to :user
default_scope { order ("created_at DESC")}
belongs_to :content, polymorphic: true
has_reputation :votes, source: :user, aggregated_by: :sum
end
photo_post.rb
class PhotoPost < ActiveRecord::Base
has_attached_file :image, styles: {
post: "200x200>"
}
end
text_post.rb
class TextPost < ActiveRecord::Base
attr_accessible :body
end
What I want, is to validate the presence of :body and :image when a users submits a text_post or photo_post respectively. So far, I found out that I have to use validates_associated.
The full project can be found on Github.
I have experiment a lot to find out how validates_associated works and searched for examples online but I don't have a clue what's going on.
(If you need any more info please let me know)
Any help/guidance is greatly appreciated.
I think for starters there needs to be some form of association between these 3 models here. You have Post, PhotoPost and TextPost. As a post can has a type of post. Also remember that type in rails is a reserved word. But anyways your models should be set out as followed:
class Post< ActiveRecord::Base
belongs_to :postable, polymorphic: true
end
class PhotoPost < ActiveRecord::Base
has_many :posts, as: :postable
end
class TextPost < ActiveRecord::Base
has_many :posts, as: :postable
end
As from looking at the snippet you provided it doesn't show any set up of polymorphic association. With regards to validating the association of the polymorphic association you might want to take a read of this answer: Validate presence of polymorphic parent. Furthermore also might want to read this article: Validating a polymorphic association for a new record . The purpose of the validate_associated validation helper simply ensures that the association between the two records are valid or as they put it works. In your case if you were to use that validation helper. This would not be what you'd want because all that would be doing is validating the association between the two models. See the second link I provided this is what I believe you are after.
Also in your controller you need to build the relationship between the models. So in your controller I think you could have something like this inside your new action:
def new
#Post = Postfind(params[:id])
#PhotoPost = #post.photoposts.build
#TextPost = #post.textposts.build
end
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.
So I am making an app that reviews books, articles and the like.
I have created the backbone of the app by creating models, views, controllers etc for Piece(the book or article), Section(self explanatory), Subsection, and Subsubsection.
I want to add a new model into the mix, a "Links" model (which will just be a link to another source or website). My issue is that I don't know how to make ALL of my previously stated models have "Links". I want each of The above models to have access and CRUD capabilities to their "Links", but so far all i have read about is has_many or has_and_belongs_to_many.
As far as I understand, those kinds of relations only relate ONE model to ONE other model, even if Piece might have many Sections, it only relates these two models.
I guess the Links model would have to have an obligatory piece_id, but then optional id's such as: section_id, subsection_id depending on where the link was. So if in Chapter 3 of my first book i want to add a link, it would have an obligatory piece_id=1 and then a section_id=3, but then no subsection_id or subsubsection_id.
So how do I go about creating a model such that it belongs to several other models? Or is this even possible?
https://github.com/kingdavidek/StuddyBuddy
Ok, it sounds like essentially you want a polymorphic association
class Link
belongs_to :linkable, polymorphic: true
end
class Piece
has_many :links, as: :linkable
end
Link would need linkable_id integer column and linkable_type string column. You can then use it in the same way as an ordinary has_many to belongs_to association
if i wanted to create a new Link in a Subsection, it would belong to
Subsection, but also to Section and Piece because of the nested
relationship
This bit rails can't help with, you'd need to write your own method to find all the links in the chain of items.
This is a pretty good use case for polymorphic associations. For simplicity lets start out with a one to many relationship:
class Link < ActiveRecord::Base
belongs_to :linkable, polymorphic: true
end
class Piece < ActiveRecord::Base
has_many :links, as: :linkable
end
class Section < ActiveRecord::Base
has_many :links, as: :linkable
end
Here the links table will have linkable_id (int) and linkable_type (string) columns. One important thing to take note of here is that linkable_id is not a true foreign key from the RBDMS point of view. Rather ActiveRecord resolves which table the relation points to when it loads the relation.
If we want to cut the duplication we can create a module which contains the desired behavior. Using ActiveSupport::Concern cuts a lot of the boilerplate code involved in creating such a module.
class Link < ActiveRecord::Base
belongs_to :linkable, polymorphic: true
end
# app/models/concerns/linkable.rb
module Linkable
extend ActiveSupport::Concern
included do
has_many :links, as: :linkable
end
end
class Piece < ActiveRecord::Base
include Linkable
end
class Section < ActiveRecord::Base
include Linkable
end
So how would we make a polymorpic many to many relation?
class Link < ActiveRecord::Base
has_many :linkings
end
# this is the join model which contains the associations between
# Links and the "linkable" models
class Linking < ActiveRecord::Base
belongs_to :link
belongs_to :linkable, polymorphic: true
end
# app/models/concerns/linkable.rb
module Linkable
extend ActiveSupport::Concern
included do
has_many :links, through: :linkings, as: :linkable
end
end
class Piece < ActiveRecord::Base
include Linkable
end
class Section < ActiveRecord::Base
include Linkable
end
On a side note - a better way to build a hierarchy between sections would be to use a single Section model and give it a self joining relationship.
class Section < ActiveRecord::Base
belongs_to :parent, class_name: 'Section'
has_many :children, class_name: 'Section',
foreign_key: 'parent_id'
end
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 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.