I have two classes:
class Comment > ActiveRecord::Base
belongs_to :tasks
end
class Work > ActiveRecord::Base
belongs_to :tasks
end
each also includes a common field called content: string (along with the standard fields for user_id, created_at, etc)
belonging to
class Task < ActiveRecord::Base
has_many :comments
has_many :works
end
What I want to do now is add a parent class called Activity for Comment and Work so that I can pull all Comments and Works from their respective tables for a specified Task containing the common fields, and I'm not sure how to back fit this in Ruby and Rails.
What you are looking for is called single table inheritance.
class Comment < Activity
belongs_to :tasks
end
class Work < Activity
belongs_to :tasks
end
You will not have a Comments table or a Works table. You will only have an Activitys table. In the table, make a column called type that holds a string. Rails considers this type column special and will know that you are referring to another model as opposed to just a string.
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 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.
I'm creating a model called Chats. And I want to assign users to a discussion. They are either a part of the Chats or they aren't...
So I create one model Chats.
What's the standard Rails naming convention for the other table?
ChatUsers?
While has_and_belongs_to_many is an ok option here, I recommend going with has_many :through instead.
In essence you will have an explicit join model, which you can call something like ChatSession.
class Chat < ActiveRecord::Base
has_many :chat_sessions
has_many :users, :through => :chat_sessions
end
class User < ActiveRecord::Base
has_many :chat_sessions
has_many :chats, :through => :chat_sessions
end
class ChatSession < ActiveRecord::Base
belongs_to :user
belongs_to :chat
end
Now you will need a table called chat_sessions with columns :user_id, and :chat_id in it. This is your join table.
Advantage
You get a model which is fully under your control, and isn't just a dumb join table managed by rails. So for example, if you want to track number of messages particular user left in particular chat, it could be a column in chat_sessions table. Presence of :through renders habtm unneeded in most cases. There is no complexity overhead either.
If it is a join table, it would be both table names joined by '_' and in alphabetical order of table names:
chats_users
This is called a has_and_belongs_to_many association in rails. You basically have two models that call has_and_belongs_to_many and create a linking table that uses the two models in the name (alphabetical and plural).
models:
class Chat < ActiveRecord::Base
has_and_belongs_to_many :users
end
class user < ActiveRecord::Base
has_and_belongs_to_many :chats
end
Then your tables would be
chats
users
chats_users
I'm trying to get the values from timestamps created in a lookup table. Let's say I've got Ingredients and Recipes, and a table called ingredients_recipes, with ingredient_id, recipe_id, and then the timestamps.
How can I get access to those timestamps? Basically, I need to know when a given ingredient was added to a recipe.
Thanks!
So you currently have something like this?
class ingredient << ActiveRecord::Base
has_and_belongs_to_many :recipes
.....
end
class recipe << ActiveRecord::Base
has_and_belongs_to_many :ingredients
....
end
And you want to get the dates they where added. The rails way to do this is by using a join model. (see http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html ) and many to many. particularly this line Choosing which way to build a many-to-many relationship is not always simple. If you need to work with the relationship model as its own entity, use has_many :through. Use has_and_belongs_to_many when working with legacy schemas or when you never work directly with the relationship itself.
So if you where to build a join model you would get something like below.
class ingredient << ActiveRecord::Base
has_many :mixtures
has_many :recipes , :through=> :mixtures
.....
end
class recipe << ActiveRecord::Base
has_many :mixtures
has_many :ingredients, :through=> :mixtures
....
end
class mixture << ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
...
end
This way you can add attributes to the mixtures table, so you can find out when they where added, who added them etc...
I am trying to model a publications. A publication can have multiple authors and editors. Since it is possible that one person is an author of one publication and an editor of another, no separate models for Authors and Editors:
class Publication < ActiveRecord::Base
has_and_belongs_to_many :authors, :class_name=>'Person'
has_and_belongs_to_many :editors, :class_name=>'Person'
end
The above code doesn't work, because it uses the same join table. Now I now that I can specify the name of the join table, but there is a warning in the API documentation is a warning about that which I don't understand:
:join_table:
Specify the name of the join table if the default based on lexical order
isn’t what you want. WARNING: If
you’re overwriting the table name of
either class, the table_name method
MUST be declared underneath any
has_and_belongs_to_many declaration in
order to work.
It means that if class Publication is linked to a table with no standard name, example "my_publications":
class Publication < ActiveRecord::Base
set_table_name "my_publication"
end
The set table name should be put behind the habtm declaration for this to work:
class Publication < ActiveRecord::Base
has_and_belongs_to_many :authors, :class_name=>'Person'
has_and_belongs_to_many :editors, :class_name=>'Person'
set_table_name "my_publication"
end
I'd generally consider this a case where you want to use has_many :through.