Is there a way to use inheritance with Ruby on Rails Active Model?
I have two models to which I want to add comments. It would be cool if I can just use one Comment model that could be associated with both models.
Look into Polymorphic Associations
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
class Article < ApplicationRecord
has_many :comments, as: :commentable
end
class Image < ApplicationRecord
has_many :comments, as: :commentable
end
Related
What is 'as' in ruby on rails model? and how does it work?
e.g.
has_many :something, as: :reasonable
Is it polymorphic?
Yes, this is a polymorphic association which allows a model to belong to multiple models. There should be
class Something < ApplicationRecord
belongs_to :reasonable, polymorphic: true
end
And then any model can have many of these as reasonable without adding another column to Something.
class Thing < ApplicationRecord
has_many :somethings, as: :reasonable
end
class Stuff < ApplicationRecord
has_many :somethings, as: :reasonable
end
Something stores both the class and ID of what its associated with allowing it to be polymorphic.
I have a comments model that is currently working with Articles. I would now like to have users be able to comment on the Coffeeshop reviews. Am I able to use the same comment table, or should I have a separate one (feels janky). I've not long been building with RoR (a few weeks) so still trying to get the hang of the basics.
Would I nest them in routes.rb (and how)
resources :coffeeshops do
resources :articles do
resources :comments
end
or
resources :coffeeshops do
resources :comments
end
resources :articles do
resources :comments
end
My models look like:
User
class User < ApplicationRecord
has_many :comments
end
Comments
class Comment < ApplicationRecord
belongs_to :user
belongs_to :article
belongs_to :coffeeshop
end
Articles
class Article < ApplicationRecord
has_many :comments, dependent: :destroy
end
Coffeeshops
class Coffeeshop < ApplicationRecord
has_many :comments, dependent: :destroy
I'm then assuming I need a foreign key to tie the user and comments together, and then also the comments to the article/coffeeshop.
I'd use a polymorphic association.
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
class User < ApplicationRecord
has_many :comments
end
class Comment < ApplicationRecord
belongs_to :user
belongs_to :commentable, polymorphic: true
end
class Article < ApplicationRecord
has_many :comments, as: :commentable
end
class Coffeeshop < ApplicationRecord
has_many :comments, as: :commentable
end
For some more information about setting up the routes/controller:
https://rubyplus.com/articles/3901-Polymorphic-Association-in-Rails-5
http://karimbutt.github.io/blog/2015/01/03/step-by-step-guide-to-polymorphic-associations-in-rails/
You can use comment model for both comments for articles and coffeeshops, but (because by default rails uses ids as primary and foreign keys I assume you use ids too) you will have to add column to comments table, where you set the comment type (You can create Enumerator in comment model, where you set 2 possible value types, each for article and coffeeshop models). If you don't add the column it will result in weird, hard to track bug where you can see comments for article on coffeeshop with same id and vise-versa.
UPD: he's little guide on using enums for rails models: http://www.justinweiss.com/articles/creating-easy-readable-attributes-with-activerecord-enums/ you will have to use it not in actual add comment form, but behind the scenes.
I have User, Guide, and CityObj models. User has one Guide, and Guide belongs to User. This allows me to use methods to access the parent from the child and vice versa:
a_user.guide
a_guide.user
But these methods aren't there for CityObj:
a_guide.cityobj ----> error
a_cityobj.guide ----> error
Maybe it has to do with the camel case? It seems like I'm doing the same thing for User/Guide and Guide/CityObj.
User.rb
class User < ActiveRecord::Base
has_one :guide, dependent: :destroy
...
end
Guide.rb
class Guide < ActiveRecord::Base
has_one :cityObj, dependent: :destroy
belongs_to :user
...
end
CityObj.rb
class CityObj < ActiveRecord::Base
belongs_to :guide
end
As per convention, you should use snake case
a_guide.cityobj
a_cityobj.guide
should be
a_guide.city_obj
a_city_obj.guide
Also, change your association in guide.rb model to below.
#guide.rb
has_one :city_obj, dependent: :destroy
As #Pavan said the convention should be snake case, including for the filenames and the association definition (and methods in general)
CitObj.rb should be city_obj.rb
User.rb should be user.rb
Also :
class Guide < ActiveRecord::Base
has_one :city_obj, dependent: :destroy
belongs_to :user
# ...
end
This will "generate" association methods like : build_city_obj etc...
Here is my models:
class User < ActiveRecord::Base
has_many :products
has_many :comments
end
class Product < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :product
end
I need to get comment records from current user products only
How do I do that? thanks
If we move the relationships to use a has_many: comments, through: products you can probably get what you're after:
class User < ActiveRecord::Base
has_many :products
has_many :comments, through: products
end
class Product < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :product
end
Now you can do user.comments.
The rails docs are here, which say:
A has_many :through association is often used to set up a many-to-many
connection with another model. This association indicates that the
declaring model can be matched with zero or more instances of another
model by proceeding through a third model. For example, consider a
medical practice where patients make appointments to see physicians.
I have three models:
data_set.rb
class DataSet < ActiveRecord::Base
has_many :browse_options
accepts_nested_attributes_for :browse_options, allow_destroy: true
end
browse_option.rb
class BrowseOption < ActiveRecord::Base
belongs_to :data_set
has_many :browse_option_datas
accepts_nested_attributes_for :browse_option_datas, allow_destroy: true
end
browse_option_data.rb
class BrowseOptionData < ActiveRecord::Base
belongs_to :browse_options
has_one :tradesman
end
I want to be able to display all the tradesman associated with a data set in the data set view with no duplicates. Is there a way I can use the joins method to do this in the controller? Thanks!
You can actually achieve this by setting up has_many through relationships between your models. There are great docs on this topic.
class DataSet
has_many :browse_options
has_many :browse_option_datas, :through => :browse_options
has_many :tradesmen, :through => :browse_option_datas
end
class BrowseOption
belongs_to :data_set
has_many :browse_option_datas
end
class BrowseOptionData
belongs_to :browse_options
belongs_to :tradesman
end
class Tradesman
has_many :browse_options_data
end
Edit: After some discussion in chat we also realised the relationships between Tradesman & BrowseOptionData needed some fixing.
Now, in your controller, you can call:
#data_set = DataSet.first
#tradesmen = #data_set.tradesmen # .uniq if you don't want any duplicates