Difference between has_many, belong_to, and both - ruby-on-rails

What's the difference in the following three scenarios?
#Case 1
class User < ActiveRecord::Base
has_many :comment
end
class Comment < ActiveRecord::Base
belong_to :user
end
Case 1 has both has_many and belong_to.
#Case 2
class User < ActiveRecord::Base
has_many :comment
end
class Comment < ActiveRecord::Base
end
Case 2 has only has_many.
#Case 3
class User < ActiveRecord::Base
end
class Comment < ActiveRecord::Base
belong_to :user
end
Case 3 has only belong_to.
Since both has_many and belong_to represent a one-to-many relation, how to we decide which of these three we should use?

They require the same database schema. The difference is only which methods are defined for you.
When you add has_many :comments to User, you gain the ability to refer to user.comments, and so easily find the comments for a particular user object (and create new ones with user.comments.build, and so on).
When you add belongs_to :user to Comment, you gain the ability to refer to comment.user, and so find the user to whom a particular comment object belongs.
These calls simply create convenience methods for you to use when manipulating your model objects. I would suggest using both, because you will likely want to use both and the relationship is clearer to someone reading the code.

Related

rails model different post types

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.

has_and_belongs_to_many STI, restrict to be unique by types

I'm using STI models with has_and_belongs_to_many relations.
So I have User with many Templates of different types, like MainTemplate < Template; NotSoMainTemplate < Template; etc.
Is there a way to limit each user to have only one MainTemplate and only one NotSoMainTemplate, and so on for all types?
Let me reiterate the problem statement as I have understood it.
You want User to have at most one kind of each template. i.e.
1 MainTemplate, 1 NotSoMainTemplate, etc.
You don't need a direct relation with Template (parent table)
Each template may be used by more than one user
Based on the above assumption, I would suggest you to do the following:
Remove existing habtm association between User and Template
Add migrations to add main_template_id, not_so_main_template_id to User
Add the following associations:
class MainTemplate < Template
has_many :users
end
class NotSoMainTemplate < Templete
has_many :users
end
class class User < ActiveRecord::Base
belongs_to :main_template
belongs_to :not_so_main_template
end
Since you are already using STI, you can try has_one.
class Template < ActiveRecord::Base
has_many :users, through: template_choice
...
end
class MainTemplate < Template
...
end
class TemplateChoice < ActiveRecord::Base
belongs_to :template_choice
belongs_to :user
end
class User < ActiveRecord::Base
has_one :main_template, through: :template_choice
has_one :not_so_main_template, through: :template_choice
...
end

Rails Associations for Lookup Table

I have a Statuses table which contains only an id and name field (Active, Inactive, Pending, etc). I then have tables such as Users, Achievements, Badges for which each of these contain a status_id foreign key. Do the associations in my models look correct?
class Status < ActiveRecord::Base
has_many :achievements
has_many :badges
has_many :users
end
class User < ActiveRecord::Base
belongs_to :status
end
class Badge < ActiveRecord::Base
belongs_to :status
end
class Achievement < ActiveRecord::Base
belongs_to :status
end
I am struggling with how to properly read the difference between has_one and has_many in the case of a lookup table. I know that a user has one company and has one profile and a company has many users but this seems backwards to me.
The simplest association setup would be:
class User < ActiveRecord::Base
has_one :status
end
That exactly describes what you have posted. Your solution would work, but it is overkill for what you've described. All the association I posted above would do is add one method to the user model, i.e.
#user = User.find(1)
#user.status
If on the other hand you wanted simple semantics for showing all the users with a particular status, THEN you'd add
class Status < ActiveRecord::Base
has_many :users
end
so now you could do:
#status = Status.find_by_description('Active').first()
#status.users
Note that in BOTH cases, all that is needed is for the users model to have an attribute 'status_id'
Belongs_to is better suited when there is an implicit hierarchy , i,e,
class Child << ActiveRecord::Base
belongs_to :parent
end

Ruby on Rails Model / Database Associations

I have a user model, farmer model, doctor model, and education model.
A farmer has a user and many educations.
A doctor has a user and many educations.
How do I setup the database for the education model?
Should it have a farmer_id AND a doctor_id?
But a education cannot belong to a farmer AND and doctor at the same time. It's one or the other.
So my education database entry would either have a farmer_id OR a doctor_id filled in, but not both.
Is there a way to guarantee that only one of the ids could be filled in at a time?
Or is there a better way to associate these models?
Your help would be appreciated!
Oh, and don't worry about the names of the models (farmer, doctor, etc.). It's just an example.
I see two possible solutions for this scenario.
The first one is to make use of polymorphic associations for education. That could look like this:
class Farmer < ActiveRecord::Base
belongs_to :user
has_many :educations, :as => :profession
end
class Doctor < ActiveRecord::Base
belongs_to :user
has_many :educations, :as => :profession
end
class Education < ActiveRecord::Base
belongs_to :profession, :polymorphic => true
end
So instead of education having a doctor_id or a farmer_id it has one profession_id and one profession_type.
The second solution would be to make use of Single Table Inheritance. And in your scenrio, that could be accomplished by letting a Doctor be a User instead of belonging to a User. And of course the same thing for a Farmer. That could look like this:
class User < ActiveRecord::Base
has_many :educations
end
class Farmer < User
end
class Doctor < User
end
class Education < ActiveRecord::Base
belongs_to :user
end
And in this scenario you would add a type column to the User model to store what type of class it is and then only having a user_id in the Education model
I think its appropriate to have the relations this way based on roles.
Class User
has_one :role
has_many :educations
end
Class Role
#What ever roles you have.
#Farmer or Doctor
belongs_to :user
end
class Education
belongs_to :user
end
This way you will store the user_id in the education object, which solves your problem.

multiple models in Rails with a shared interface

I'm not sure of the best structure for a particular situation in Rails. We have several types of workshops. The administration of the workshops is the same regardless of workshop type, so the data for the workshops is in a single model. We collect feedback from participants about the workshops, and the questionnaire is different for each type of workshop. I want to access the feedback about the workshop from the workshop model, but the class of the associated model will depend on the type of workshop. If I was doing this in something other than Rails, I would set up an abstract class for WorkshopFeedback, and then have subclasses for each type of workshop: WorkshopFeedbackOne, WorkshopFeedbackTwo, WorkshopFeedbackThree. I'm unsure how to best handle this with Rails.
I currently have:
class Workshop < ActiveRecord::Base
has_many :workshop_feedbacks
end
class Feedback < ActiveRecord::Base
belongs_to :workshop
has_many :feedback_ones
has_many :feedback_twos
has_many :feedback_threes
end
class FeedbackOne < ActiveRecord::Base
belongs_to :feedback
end
class FeedbackTwo < ActiveRecord::Base
belongs_to :feedback
end
class FeedbackThree < ActiveRecord::Base
belongs_to :feedback
end
This doesn't seem like to the cleanest way to access the feedback from the workshop model, as accessing the correct feedback will require logic investigating the Workshop type and then choosing, for instance, #workshop.feedback.feedback_one.
Is there a better way to handle this situation? Would it be better to use a polymorphic association for feedback? Or maybe using a Module or Mixin for the shared Feedback interface?
Note: I am avoiding using Single Table Inheritance here because the FeedbackOne, FeedbackTwo, FeedbackThree models do not share much common data, so I would end up with a large sparsely populated table with STI.
I think the best solution is create an abstract class Workshop, and 3 subclasses Workshop1, Workshop2 and Workshop3.
Hence, each will have its set of feedbacks, Feedback1 to Workshop1, Feedback2 to Workshop2, ...
You can change the declaration in the subclasses as follows:
class Workshop1 < Workshop
has_many :feedbacks, :class_name => "Feedback1"
end
class Feedback1 < ActiveRecord::Base
belongs_to :workshop, :class_name => "Workshop1"
end
And in your application can use workshop.feedbacks and and feedback.workshop no matter what class the instance of the Workshop or Feedback belongs.
EDIT: You have three types of workshop with information in common, but each workshop has a specific kind of feedback. So it's bestter to use STI for Workshop, and not for Feedback.
You can subclass your models, like so:
class Workshop < ActiveRecord::Base
has_many :feedback_ones
has_many :feedback_twos
has_many :feedback_threes
#has_many :feedbacks # This MIGHT work, but is untested. I'm not at a dev setup to try.
end
class Feedback < ActiveRecord::Base
belongs_to :workshop
has_many :feedback_ones
has_many :feedback_twos
has_many :feedback_threes
end
class FeedbackOne < Feedback
belongs_to :feedback
end
class FeedbackTwo < Feedback
belongs_to :feedback
end
class FeedbackThree < Feedback
belongs_to :feedback
end
This is likely a better solution, and is cleaner. Your Feedback table needs to have a type column, which it uses to use one table for multiple classes. This blog post gives a good basic introduction to the concept of Single Table Inheritance.

Resources