I have two models Board and Pictures and I want a user to be able to comment on either the Board as a whole or the individual Pictures.
My polymorphic Comment model:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
and then I have a simple has_many in each of the other models:
class Board < ActiveRecord::Base
has_many :comments, as: :commentable, dependent: :destroy
has_many :pictures
class Pictures < ActiveRecord::Base
has_many :comments, as: :commentable, dependent: :destroy
belongs_to :board
What I really want to be able to do is add a new scope called something like all_comments which combines the comments from the Board model with the related comments from the Pictures model. I can do this with a method, but I end up with an array and not a scoped relation.
My method:
def all_comments
comments = self.comments
return comments + self.pictures.map {|x| x.comments}.flatten
end
How can I return a scoped relationship that can chain the results?
first of all Pictures should be singular.
secondly, you can just call a finder on Comment to get all the comments you want
Comment.where("type = 'board' AND id IN(?) OR type = 'picture' AND id IN(?)", self.id, self.pictures.map(&:id))
or something like that.
phoet's answer intrigued me (I +1'd), so here's my refactor / expansion on what he suggested:
You'd look in the Comment model to pull out the relevant comments. To do this, you need to firstly know the board & the pictures associated with it. Phoet uses the self.pictures object for this:
#app/models/comment.rb
Class Board < ActiveRecord::Base
def all_comments
ids = self.id + self.pictures.map(&:id)
Comment.find(ids)
end
end
This will find the ids in the comment model, returning the data as a collection. If you wanted a true representation of the comments (hierarchical), you'd have to use some sort of ancestry / inheritance structure I think
Related
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
I'm trying to determine the proper ActiveModel realtionship for the following situation: there are pictures and there are different categories of them: foo, bar, baz and qux. A User can set one like and several comments per the picture.
I started with a Catregory and a Foo models. The simpliest approach could be - creatation of likes and comments properties for each of the Foo, Bar, Baz and Qux models. But I feel it's a silly approach... There might be a better one.
What is the best kind of realationship can be chosen for such a case?
Here's what I imagined reading your question (I've decided that your pictures were created by users, because your model looks a lot like a social network):
class User < ActiveRecord::Base
has_many :pictures
has_many :comments
has_one :like
end
class Picture < ActiveRecord::Base
belongs_to :user
belongs_to :category
has_many :likes
has_many :comments
end
class Category < ActiveRecord::Base
has_many :pictures
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :picture
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :picture
end
Categories are rows of the categories Table. Instead of making one Model per category, you should have one model Category for all of them, with attributes like name, color or whatever you want.
Hi I have a comment object, and I use polymorphic association, so it can belongs to many other objects. But I also want them to belong to users.
Now I can, call comment.comment_owner and I get the object that was commented by this comment.
As for the user I have a user_id field in the comment object, I pass the user id through the form. But when I try to get owner user by comment.user I get an error. Right now I`m getting user by User.find(comment.user_id). But this looks bad.
Is there a way to pass the user id. So I can get User owning a comment by comment.user
My associations:
class Comment < ActiveRecord::Base
belongs_to :comment_owner, polymorphic: true
end
class User < ActiveRecord::Base
has_many :comments, as: :comment_owner
end
class Posts < ActiveRecord::Base
has_many :comments, as: :comment_owner
end
Why not just
class Comment < ApplicationRecord
belongs_to :user
end
First of all, in my opinion comment_owner is not a good name for what you're designing here. Comment owner would suggest an ownership relation (rather a person or someone). I'd rather call it commentable as these object are being commented on.
If this relation is meant to be a polymorphic then you should have commentable_type and commentable_id (or comment_owner_type and comment_owner_id if you really prefer the original) as polymorphic => true expects to have those two fields (named as: relation_name_type and relation_name_id).
If you then have a Comment object you get the user commented by calling comment.commentable (or comment.comment_owner in case you decide to keep your naming).
[EDIT]
As you said, you want to have two parents. If I get this right you just want to have two relations - this means that if you just modify your code to:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
belongs_to :author, class_name: 'User'
end
class User < ActiveRecord::Base
has_many :comments, as: :commentable
has_many :notes, class_name: 'Comment'
end
class Post < ActiveRecord::Base
has_many :comments, as: :commentable
end
You will have your polymorphic relation as well as the ownership.
I'm having trouble figuring out the proper way of retrieving all children of multiple parents through association chaining.
To simplify I have three models:
class Customer < ActiveRecord::Base
has_many :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :invoice
end
After creating a few objects I tired to use the example from rails guides (association basics: 4.3.3.4 includes):
Customer.first.invoices.line_items
It returns:
undefined method `line_items' for #<Customer::ActiveRecord_Associations_CollectionProxy
Is grandparent.parents.children not usable?
EDIT
I'm not searching for the grandparent.parents.first.children, but all children of all parents in the collection, rails guides state:
If you frequently retrieve line items directly from customers (#customer.orders.line_items),
As a valid operation, I would like to know if that is a mistake.
FINAL As stated in the comments of the selected answer: in ActiveRecord: scopes are chainable but associations are not.
The customer.invoices.line_items cannot work the way you want to, since the has_many always is linked to a single record. but you can achieve what you want (if I understand correctly) using has_many through
as follows:
class Customer < ActiveRecord::Base
has_many :invoices
has_many :line_items, through: :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :invoice
end
and now you can write:
customer.line_items
and it will return all line_items which are connected to a customer's invoices.
Customer.first.invoices.first.line_items
Or if you want all of the data together, you can do something like:
results = Customer.first.invoices.includes(:line_items)
Then you may access data with no DB call, by looping results. For first data ex: results.first.line_items
Hope it helps!
Customer.first.invoices will return an collection (like an array) of invoices. The line_items method isn't defined for a collection, but its defined for an invoice. Try Customer.first.invoices.first.line_items
EDIT - If you always want the orders to include the line items, you can just do:
class Customer < ActiveRecord::Base
has_many :orders, -> { includes :line_items }
end
class Order < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :order
end
I want to model different post types
ImagePost VideoPost TextPost. They all have different contents
I was going to go with post has_many polymorphic but rails doesn't support it
A previous stackoverflow post pointed me towards has_many_polymorphs gem but is deprecated
I need to be able to post different posts types and retrieve them in an instance show them on a feed
e.g.
#posts.each do ..
if type == video ...
elseif type == image ....
I'm new to rails so thanks for the assistance.
Use single table inheritance of Post model
class class Post < ActiveRecord::Base
.....
end
Than inherit this Post model into these model.
class VideoPost < Post
end
class ImagePost < Post
end
At migration you need to create a type column for different type of post. For details look at this blog post
Consider doing the following
class Post < ActiveRecord::Base
# Create the an association table and add additional info on the association table, description, etc etc.
has_many :images, through: image_posts
has_many :image_posts
end
class Image < ActiveRecord::Base
# Image specific
end
Doing this, #post.image_posts.count > 0 indicates there are multiple image_posts.
Or you could also achieve the goal by polymorphic relation:
class VideoPost < ActiveRecord::Base
belongs_to :postable, polymorphic: true
end
class ImagePost < ActiveRecord::Base
belongs_to :postable, polymorphic: true
end
class Feed < ActiveRecord::Base
has_many :posts, as: :postable
end
In this case #feed.posts.each will check the postable_type, which is the model type instead.
STI is the way to go. I guess columns of all three type should be same or similar at least. So Single tale inheritance would be the best choice.