I have 3 models: User, Object, Likes
Currently, I have the model: a user has many Objects. How do I go about modeling:
1) A user can like many objects
2) an Object can have many likes (from different users)
So I want to be able to do something like this:
User.likes = list of objects liked by a user
Objects.liked_by = list of Users liked by object
The model below is definitely wrong...
class User < ActiveRecord::Base
has_many :objects
has_many :objects, :through => :likes
end
class Likes < ActiveRecord::Base
belongs_to :user
belongs_to :object
end
class Objects < ActiveRecord::Base
belongs_to :users
has_many :users, :through => :likes
end
To elaborate further on my comment to Brandon Tilley's answer, I would suggest the following:
class User < ActiveRecord::Base
# your original association
has_many :things
# the like associations
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
# your original association
belongs_to :user
# the like associations
has_many :likes
has_many :liking_users, :through => :likes, :source => :user
end
You are close; to use a :through, relation, you first must set up the relationship you're going through:
class User < ActiveRecord::Base
has_many :likes
has_many :objects, :through => :likes
end
class Likes < ActiveRecord::Base
belongs_to :user
belongs_to :object
end
class Objects < ActiveRecord::Base
has_many :likes
has_many :users, :through => :likes
end
Note that Objects should has_many :likes, so that the foreign key is in the right place. (Also, you should probably use the singular form Like and Object for your models.)
Here is a simple method to achieve this. Basically, you can create as many relationships as needed as long as you specify the proper class name using the :class_name option. However, it is not always a good idea, so make sure only one is used during any given request, to avoid additional queries.
class User < ActiveRecord::Base
has_many :likes, :include => :obj
has_many :objs
has_many :liked, :through => :likes, :class_name => 'Obj'
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :obj
end
class Obj < ActiveRecord::Base
belongs_to :user
has_many :likes, :include => :user
has_many :users, :through => :likes
# having both belongs to and has many for users may be confusing
# so it's better to use a different name
has_many :liked_by, :through => :likes, :class_name => 'User'
end
u = User.find(1)
u.objs # all objects created by u
u.liked # all objects liked by u
u.likes # all likes
u.likes.collect(&:obj) # all objects liked by u
o = Obj.find(1)
o.user # creator
o.users # users who liked o
o.liked_by # users who liked o. same as o.users
o.likes # all likes for o
o.likes.collect(&:user)
Models & associations as per naming conventions of rails modeling
class User < ActiveRecord::Base
has_many :likes
has_many :objects, :through => :likes
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :object
end
class Object < ActiveRecord::Base
belongs_to :user
has_many :likes
has_many :users, :through => :likes
end
Also, you can use of already built-in gems like acts-as-taggable-on to have same functionality without code :)
Related
A list has one owner (a user). A list also has a number of panelists (also users). I have tried defining the relationships between the three models: User, List, and Panelist. But I'm getting nowhere.
user.rb
class User < ActiveRecord::Base
has_many :lists
has_many :panelMemberships, :through => :panelists, :source => :lists
end
list.rb
class List < ActiveRecord::Base
belongs_to :user
has_many :panelMembers, :through => :panelists, :source => :user
end
panelist.rb
class Panelist < ActiveRecord::Base
belongs_to :list
belongs_to :user
end
I've tried all different combinations but nothing seems to work. Thanks in advance for any help you can provide.
The model also has to have a has_many relationship for whatever the through model is, so wherever you have has_many :x, through: :y, you also need to say has_many :y. You also shouldn't have a panelist model separate from your user model if panelists are users (unless you're doing STI, which you're not). From what I understand, you're trying to do something like this:
class User < ActiveRecord::Base
has_many :owned_lists, class_name: "List", foreign_key: :owner_id # this is for the owner/list relationship
has_and_belongs_to_many :lists # for the normal panelist / list relationship
end
class List < ActiveRecord::Base
belongs_to :owner, class_name: "User"
has_and_belongs_to_many :users
end
Then you'll need to make a migration for a users_lists (with user id and list id) table which will be your join table but won't need its own model. But if you really want to keep the through relationship (good for if you do other stuff with the join model), then you'd do:
class User < ActiveRecord::Base
has_many :owned_lists, class_name: "List", foreign_key: :owner_id # this is for the owner/list relationship
has_many :panel_memberships
has_many :lists, through: :panel_memberships
end
class List < ActiveRecord::Base
belongs_to :owner, class_name: "User"
has_many :panel_memberships
has_many :users, through: :panel_memberships
end
class PanelMembership < ActiveRecord::Base
belongs_to :user
belongs_to :list
How should I create following model in Rails 3.2? Project can have 1+ owners and 1+ users. Both of them are instances of class Person. I've thought about has_and_belongs_to_many but I don't know how to handle two separate collections of Persons for each Project.
You'll need a join model to represent each has-and-belongs-to-many relationship, and you would access using has-many-through as described here:
class ProjectOwnerLink < ActiveRecord::Base
belongs_to :project
belongs_to :owner, class_name: 'Person'
end
class ProjectUserLink < ActiveRecord::Base
belongs_to :project
belongs_to :user, class_name: 'Person'
end
class Project < ActiveRecord::Base
has_many :project_owner_links
has_many :owners, :through => :project_owner_links
has_many :project_user_links
has_many :users, :through => :project_user_links
end
class Person < ActiveRecord::Base
has_many :project_owner_links
has_many :owned_projects, :through => :project_owner_links, :source => :project
has_many :project_user_links
has_many :used_projects, :through => :project_user_links, :source => :project
end
You could define another model Participation that holds the type of the relationship, i.e. the role of the user. (Untested) code:
class Project < ActiveRecord::Base
has_many :participations
has_many :users, :through => :participations
def with_role(role)
includes(:participations).where('participation.role = ?', role)
end
def owners
users.with_role('owner')
end
def participants
users.with_role('participant')
end
end
class User < ActiveRecord::Base
has_many :participations
has_many :projects, :through => :participations
def with_role(role)
includes(:participations).where('participation.role = ?', role)
end
def projects_owned
projects.with_role('owner')
end
def projects_participating_in
projects.with_role('participant')
end
end
class Participation < ActiveRecord::Base
# has an attribute 'role'
belongs_to :project
belongs_to :user
end
Below is the demo application.
https://github.com/diatmpravin/habtm-demo.git
Please have a look, Let me know if you have any question?
Imagine 4 models in Rails 3.1
class Student < ActiveRecord::Base
has_many :memberships
has_many :courses, :through => :memberships
has_many :tests, :through => :courses
end
class Membership < ActiveRecord::Base
belongs_to :student
belongs_to :course
end
class Course < ActiveRecod::Base
has_many :tests
has_many :students, :through => :memberships
end
class Test < ActiveRecord::Base
belongs_to :course
end
How can I output a sorted list (ie by date) of a student's upcoming tests
(I'm guessing there is a fairly simple answer, but I've been trying in vain for a while)
My best guess is something like:
#upcomingTests = #currstudent.tests.sort_by &:testDateTime
but it seems to return an empty array
First of all, there is an slight error on your model "Course". It needs "belongs_to :student".
class Course < ActiveRecod::Base
has_many :tests
has_many :students, :through => :memberships
belongs_to :student
end
After you've created and populated a foreign key, you can create a simple named_scope on your test mode:
named_scope :ordered, :order => "created_at DESC"
Then it's just the matter of accessing it from wherever you want:
#ordered_tests = #student.tests.ordered
Right now I have a rich many-to-many association with VideoVote as the independent record.
class VideoVote < ActiveRecord::Base
belongs_to :user
belongs_to :video
end
class User < ActiveRecord::Base
has_many :video_votes
has_many :voted_videos,
:through => :video_votes,
:source => :video
end
class Video < ActiveRecord::Base
has_many :video_votes
has_many :voted_users,
:through => :video_votes,
:source => :user
end
However, I want to trasform this into a polymorphic association where comments can also have many VideoVotes (I realize this is confusing, so I should probably change it to Votes). (also, a video will have many comments.) How should I do this?
You first want to add voteable_id:integer and voteable_type:string to your video_votes table.
Then your models will look like:
class VideoVote < ActiveRecord::Base
belongs_to :voteable, :polymorphic => true
end
class Comment < ActiveRecord::Base
has_many :video_votes, :as => :voteable
#code
end
class Video < ActiveRecord::Base
has_many :video_votes, :as => :voteable
#code
end
Then you can access them just like any other has_many:
#video.video_votes
#comment.video_votes
#etc.
I am new to rails, and have a situation that I can't quite get my head around.
Lets say I have two resources, users and widgets.
Users can use widgets, but widgets are also user created, and should be owned by the user that created them. There needs to be a user uses widget, and a user owns widget. Is the following what I am looking for?
Class User < ActiveRecord::Base
has_many :uses
has_many :widgets, :through => :uses
has_many :owns
has_many :widgets, :through => :owns
end
Class Widget < ActiveRecord::Base
has_one :own
has_many :uses
has_many :users, :through => :uses
end
Class Use < ActiveRecord::Base
belongs_to :user
belongs_to :widget
end
Class Own < ActiveRecord::Base
belongs_to :user
belongs_to :widget
end
I would do it slightly different.
Class User < ActiveRecord::Base
has_many :uses
has_many :widgets, :through => :uses
has_many :owned_widgets, :class_name => "Widget"
end
Class Widget < ActiveRecord::Base
belongs_to :owner, :class_name => "User"
has_many :uses
has_many :users, :through => :uses
end
Class Use < ActiveRecord::Base
belongs_to :user
belongs_to :widget
end
I changed the names a bit, you had a name conflict on widget, you cannot have two associations with the same name. I also removed the has_one and just set an owner which will have a foreign_key of owner_id with the class set to User. Other than that, you set the many-to-many relationship up nicely.