Modeling "Likes" in rails HABTM vs HM/BT - ruby-on-rails

What would be the best method to model "likes" in rails for my app. I could either to the following:
class User < ActiveRecord::Base
has_many :things
has_many :likes
has_many :liked_things, through: :likes, source: :thing
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :thing
end
class Thing < ActiveRecord::Base
belongs_to :user
has_many :likes
has_many :liking_users, through: :likes, source: :user
end
Or
class User < ActiveRecord::Base
has_many :things
has_and_belongs_to_many :things
end
class Thing < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :users
end
What approach would be best and why? I plan to have an activity feed in my app as well, if that helps determine the best approach.

The answer to this question depends on whether or not Like will ever have any attributes or methods.
If its only purpose of existence is to be the HABTM relationship between Users and Things, then using the has_and_belongs_to_many relationship would suffice. In your example, having has_many and belongs_to is redundant. All you would need in this case is:
class User < ActiveRecord::Base
has_and_belongs_to_many :things
end
class Thing < ActiveRecord::Base
has_and_belongs_to_many :users
end
On the other hand, if you anticipate that a Like will have an attribute (e.g. maybe someone will really like something, or love it, etc.) then you can do
class User < ActiveRecord::Base
has_many :likes
has_many :liked_things, through: :likes, source: :thing
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :thing
end
class Thing < ActiveRecord::Base
has_many :likes
has_many :liking_users, through: :likes, source: :user
end
Note that I removed has_many :things and belongs_to :user as they are redundant.

Related

How to setup model associations in Rails

I'm trying to create associations for three models in my Rails application. In the application a User can access courses which have videos. How would I model this?
This is what I currently have:
class User < ApplicationRecord
has_many :courses
has_many :videos, through: :courses
end
class Course < ApplicationRecord
belongs_to :user
has_many :videos
end
class Video < ApplicationRecord
belongs_to :course
belongs_to :user
end
Is this the correct way to model these associations for what I want the application to be able to achieve?
Normally, this would look something like:
class UserCourse < ApplicationRecord
belongs_to :user
belongs_to :course
end
class User < ApplicationRecord
has_many :user_courses
has_many :courses, through: :user_courses
has_many :videos, through: :courses
end
class Course < ApplicationRecord
has_many :user_courses
has_many :users, through: :user_courses
has_many :videos
end
class Video < ApplicationRecord
belongs_to :course
has_many :users, through: :course
end
That should let you do:
#user.courses
#user.videos
#course.users
#course.videos
#video.course
#video.users
(Assuming, of course, you've instantiated each of the above variables and you have associated records.)

Association between 3 models

I would like a user be able to create a course(so it should belong to one user) and also be able to join another course that it haven't created by him.What is the proper associations between the course and the user ? I want to make the following model associations:
Class User < ActiveRecord::Base
has_many :courses
has_many :comments ,through: :courses
end
Class Course < ActiveRecord::Base
has_and_belongs_to_many :users #here i am not sure
has_many :comments
end
Class Comment < ActiveRecord::Base
belongs_to :courses
end
I think what you should be able to do something like:
Class User < ActiveRecord::Base
has_many :courses
has_many :course_users
has_many :subscribed_courses, through: :course_users, source: :course # I think you should be able to do foreign_key: :course_id, class_name: 'Course'
has_many :comments ,through: :courses
end
Class Course < ActiveRecord::Base
belongs_to :user
has_many :course_users
has_many :participants, through: :course_users, source: :user # I think you should be able to do foreign_key: :user_id, class_name: 'User'
has_many :comments
end
Class Comment < ActiveRecord::Base
belongs_to :courses
end
#course_users is a join table for courses and users
class CourseUser < ActiveRecord::Base
# inside here you could have several other connections e.g grade of a user in a course within this join model
belongs_to :user
belongs_to :course
end
If I'm understanding what you're saying - you need to have a third model - you can call it enrollment
For Course you would use belongs_to :user if each course is created as a user.
Your Enrollment model with have two HABTAM
Class Enrollment < ActiveRecord::Base
has_and_belongs_to_many :users
has_and_belongs_to_many :courses
end
(An Aside, if a course is going to be offered more than once, you'll have to add an additional model for each instance of the course and the enrollment will belong to that model, and not courses)

Active Record Associations: has_and_belongs_to_many, has_many :through or polymorphic association?

The Ruby on Rails app I am working on allows users to create and share agendas with other users.
In addition, we must be able to:
Display a list of agendas for each user, on his profile
Display a list of users associated with an agenda, on the agenda's page
When sharing an agenda with another user, define a role for this user, and display the role of this user on the list mentioned right above
I was going to go with a has_and_belongs_to_many association between the user and the agenda models, like that:
class User < ActiveRecord::Base
has_and_belongs_to_many :agendas
end
class Agenda < ActiveRecord::Base
has_and_belongs_to_many :users
end
But then I wondered whether this would let me get and display the #user.agenda.user.role list of roles on the given agenda page of a given user.
And I thought I should probably go with a has_many :through association instead, such as:
class User < ActiveRecord::Base
has_many :roles
has_many :agendas, through: :roles
end
class Role < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
end
class Agenda < ActiveRecord::Base
has_many :roles
has_many :users, through: :roles
end
And although I was pretty comfortable about the idea of a user having several roles (one for each agenda), I am not sure about the idea of an agenda having several roles (one for each user?).
Finally, to add to the confusion, I read about the polymorphic association and thought it could also be a viable solution, if done this way for instance:
class Role < ActiveRecord::Base
belongs_to :definition, polymorphic: true
end
class User < ActiveRecord::Base
has_many :roles, as: :definition
end
class Agenda < ActiveRecord::Base
has_many :roles, as: :definition
end
Does any of the above solutions sound right for the situation?
UPDATE: Doing some research, I stumbled upon this article (from 2012) explaining that has_many :through was a "smarter" choice than has_and_belongs_to_many. In my case, I am still not sure about the fact that an agenda would have many roles.
UPDATE 2: As suggested in the comments by #engineersmnkyn, a way of solving this would be to go with two join tables. I tried to implement the following code:
class User < ActiveRecord::Base
has_many :agendas, through: :jointable
end
class Agenda < ActiveRecord::Base
end
class Role < ActiveRecord::Base
end
class Jointable < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
has_many :agendaroles through :jointable2
end
class Jointable2 < ActiveRecord::Base
belongs_to :roles
belongs_to :useragenda
end
I am not sure about the syntax though. Am I on the right track? And how should I define the Agenda and the Role models?
UPDATE 3: What if I went with something like:
class User < ActiveRecord::Base
has_many :roles
has_many :agendas, through: :roles
end
class Role < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
end
class Agenda < ActiveRecord::Base
has_many :roles
has_many :users, through: :roles
end
and then, in the migration file, go with something like:
class CreateRoles < ActiveRecord::Migration
def change
create_table :roles do |t|
t.belongs_to :user, index: true
t.belongs_to :agenda, index: true
t.string :privilege
t.timestamps
end
end
end
Would I be able to call #user.agenda.privilege to get the privilege ("role" of creator, editor or viewer) of a given user for a given agenda?
Conversely, would I be able to call #agenda.user.privilege ?
Okay I will preface by saying I have not tested this but I think one of these 2 choices should work well for you.
Also if these join tables will never need functionality besides a relationship then has_and_belongs_to_many would be fine and more concise.
Basic Rails rule of thumb:
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.
First using your example (http://repl.it/tNS):
class User < ActiveRecord::Base
has_many :user_agendas
has_many :agendas, through: :user_agendas
has_many :user_agenda_roles, through: :user_agendas
has_many :roles, through: :user_agenda_roles
def agenda_roles(agenda)
roles.where(user_agenda_roles:{agenda:agenda})
end
end
class Agenda < ActiveRecord::Base
has_many :user_agendas
has_many :users, through: :user_agendas
has_many :user_agenda_roles, through: :user_agendas
has_many :roles, through: :user_agenda_roles
def user_roles(user)
roles.where(user_agenda_roles:{user: user})
end
end
class Role < ActiveRecord::Base
has_many :user_agenda_roles
end
class UserAgenda < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
has_many :user_agenda_roles
has_many :roles, through: :user_agenda_roles
end
class UserAgendaRoles < ActiveRecord::Base
belongs_to :role
belongs_to :user_agenda
end
This uses a join table to hold the relationship of User <=> Agenda and then a table to join UserAgenda => Role.
The Second Option is to use a join table to hold the relationship of User <=> Agenda and another join table to handle the relationship of User <=> Agenda <=> Role. This option will take a bit more set up from a CRUD standpoint for things like validating if the user is a user for that Agenda but allows a little flexibility.
class User < ActiveRecord::Base
has_many :user_agendas
has_many :agendas, through: :user_agendas
has_many :user_agenda_roles
has_many :roles, through: :user_agenda_roles
def agenda_roles(agenda)
roles.where(user_agenda_roles:{agenda: agenda})
end
end
class Agenda < ActiveRecord::Base
has_many :user_agendas
has_many :users, through: :user_agendas
has_many :user_agenda_roles
has_many :roles, through: :user_agenda_roles
def user_roles(user)
roles.where(user_agenda_roles:{user: user})
end
end
class Role < ActiveRecord::Base
has_many :user_agenda_roles
end
class UserAgenda < ActiveRecord::Base
belongs_to :user
belongs_to :agenda
end
class UserAgendaRoles < ActiveRecord::Base
belongs_to :role
belongs_to :user
belongs_to :agenda
end
I know this is a long answer but I wanted to show you more than 1 way to solve the problem in this case. Hope it helps

How to correctly write my relationship/associations in Rails?

I have 4 Models and i am not sure what is the correct way to write my relationships/associations.
class User < ActiveRecord::Base
has_many :boards
has_many :lists
has_many :cards
end
class Board < ActiveRecord::Base
belongs_to :user
has_many :lists
end
class List < ActiveRecord::Base
belongs_to :user
belongs_to :board
has_many :cards
end
class Card < ActiveRecord::Base
belongs_to :user
belongs_to :list
end
If you want to be more explicit about your relationships, feel free to do the following (preferred in most every case):
class User < ActiveRecord::Base
has_many :boards, inverse_of: :user, dependent: :destroy
has_many :lists, inverse_of: :user, dependent: :destroy
has_many :cards, inverse_of: user, dependent: :destroy
end
class Board < ActiveRecord::Base
belongs_to :user, inverse_of: :boards
has_many :lists, inverse_of: :board
end
class List < ActiveRecord::Base
belongs_to :user, inverse_of: :lists
belongs_to :board, inverse_of :lists
has_many :cards, inverse_of :list
end
class Card < ActiveRecord::Base
belongs_to :user, inverse_of: :cards
belongs_to :list, inverse_of :cards
end
Finally, make sure any of your models that are dependent on another (e.g. Board belongs_to User) have the appropriate foreign key in their table. So, for example, Board will need to have a user_id foreign key to correctly create the association.
You can create a migration for any of those entities if you haven't already like so:
rails generate migration AddUserRefToBoards user:references

issue with has_many, through association

For some reason, my has_many through association isn't working. Here are my models:
class Interest < ActiveRecord::Base
has_many :evints
has_many :events, through: :evints
has_many :images, through: :events
end
class Event < ActiveRecord::Base
has_many :evints
has_many :images
has_many :interests, through: :evints
end
class Evint < ActiveRecord::Base
belongs_to :events
belongs_to :interests
end
The Evints table has three columns: interest_id, event_id, and id.
When I call #interest.events, I get the error message
uninitialized constant Interest::Events
Obviously, there's something going wrong with the association if #interest.events is being read as a constant!
Does anyone have any ideas?
Thanks!
Check your Evint class, it should be:
class Evint < ActiveRecord::Base
belongs_to :event
belongs_to :interest
end
On a different note, I think Evint isn't really a very good name. It'd suggest that you go with EventInterest, and name the table event_interests.

Resources