Has_and_belong_to_many relation with the same model - ruby-on-rails

I am trying to implement a has_and_belong_to_many relation with the same model, but I don't know how.
For Example, a user should be able to follow other users.
Also, I have multiple fields of the same model in that model; how would I give it another name?

There are two scenarios and two different implementations:
The 'Friends' model
Let's say one User can have many :friends where each friend is also an object of the User model. You can do it this way:
has_and_belongs_to_many :friends, class_name: 'User'
This tells rails that an object of User class can have a many-to-many relation with itself as friends. So you can call something like this:
#user_a.friends
#=> [#user_x, #user_y, #user_z] # Array of User Objects
#user_x.friends
#=> [#user_a, #user_b, #user_c] # Array of User Objects
The 'Followers/Following' model
Let's say one User can follow other users as well have other users follow him. This is how you'll implement it:
has_many :followers, class_name: 'User', inverse_of: :following
belongs_to :following, class_name: 'User', inverse_of: :followers
This tells rails that each user can have many followers which is an Array of other User objects, and that user is accessible to others as an object in the following array. For example, if #user2 follows #user1, it would look like this:
#user1.followers
#=> [#user2, #user3]
#user2.following
#=> [#user1]

Related

Rails 4 return tasks associated with more than one user

Question: How can I return Assignments associates with one of many users in the users array?
I researched the Rails guides and some only posts but I can't figure this out yet.
https://codereview.stackexchange.com/questions/46319/is-there-a-better-approach-to-searching-has-and-belongs-to-many-relations
http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
http://guides.rubyonrails.org/active_record_querying.html#retrieving-multiple-objects-in-batches
I am associating users to assignments two different ways.
1- user "user_id" is the one who creates the assignment
2- The assignment is given to multiple users. Users are associated to assignments using has_and_belongs_to_many :users
Basically each assignment is associated to the user who owns the task.
The assignment is also given to multiple users who will work on it.
I can successfully return all tasks associated with user but not with users.
I'm trying to display only the tasks associated with the current_user (devise)
This works for user:
assignment.rb
class Assignment < ActiveRecord::Base
belongs_to :deliverable
belongs_to :user
has_and_belongs_to_many :users
end
user.rb
has_and_belongs_to_many :assignments
The association are working fine. In the console I can get all the users associated via HABTM to the assignment.
I have a designer dashboard where i only want to display assignments given to the current user.
designer_dashboard controller:
#if I do this I'll get all the assignments:
#assignments = Assignment.all
# but I want to be able to do something like this to get only the assingments associated with the current user via HABTM
#assignments = Assignment.includes(:users).where(["user_ids =?", current_user])
The data isn't modelled to reflect the 2 different types of relationship that exist between users and tasks - task owners and task designers. You've only set up one of them.
You will need to remodel the data and give the relationships more meaningful names.
One way would be to use has_many_through for the association that a Task has with who its assigned to. A Task has_many Designers through AssignedTasks. The association between Task and User can be named as TaskOwner. If you set up both these associations you will be able to get current_user.owned tasks and current_user.assigned tasks which is more clear than referring to user and users.
class Task
belongs_to :task_owner, class_name: "User"
has_many :assigned_tasks
has_many :designers, through: assigned_tasks, class_name: "User"
end
class User
has_many :owned_tasks, class_name: "Task"
has_many :assigned_tasks, foreign_key: designer_id
has_many :tasks, through: :assigned_tasks,
end
class AssignedTask
belongs_to :designer
belongs_to :task
end
You will need to generate some migrations to add the requisite ids.
Also, I seem to remember reading somewhere that Task is a reserved word. You may want to rename task to something else.
Margo answer is correct.
This works in controller:
#assignments = current_user.assignments

Ruby on Rails multiple associations between two models

Here is my problem:
I have a model User. a User may be admin.
A user can send error report to a pool of reports and this report can be assigned to an admin
How would you link these two models (User/Report) knowing that an admin is a user?
I'd want a report.sender and a report.admin but I can't find how to do so.
Thanks!
You can define the relations like this:
class Report < ActiveRecord::Base
belongs_to :reporter, foreign_key: reporter_id, class_name: 'User'
belongs_to :admin, foreign_key: admin_id, class_name: 'User'
And use them like that:
report.admin # => returns a User object
report.reporter # => also returns a User object

Custom scope on has_many, :through association (Rails 4)

CONTEXT:
In my setup Users have many Communities through CommunityUser, and Communities have many Posts through CommunityPost. If follows then, that Users have many Posts through Communities.
User.rb
has_many :community_users
has_many :communities, through: :community_users
has_many :posts, through: :communities
Given the above User.rb, Calling "current_user.posts" returns posts with one or more communities in common with current_user.
QUESTION:
I'm looking to refine that association so that calling "current_user.posts" returns only those posts whose communities are a complete subset of the current_user's communities.
So, given a current_user with community_ids of [1,2,3], calling "current_user.posts" would yield only those posts whose "community_ids" array is either 1, [2], [3], [1,2], [1,3], [2,3], or [1,2,3].
I've been researching scopes here, but can't seem to pinpoint how to accomplish this successfully...
Nice question...
My immediate thoughts:
--
ActiveRecord Association Extension
These basically allow you to create a series of methods for associations, allowing you to determine specific criteria, like this:
#app/models/user.rb
has_many :communities, through: :community_users
has_many :posts, through: :communities do
def in_community
where("community_ids IN (?)", user.community_ids)
end
end
--
Conditional Association
You could use conditions in your association, like so:
#app/models/user.rb
has_many :posts, -> { where("id IN (?)", user.community_ids) }, through: :communities #-> I believe the model object (I.E user) is available in the association
--
Source
I originally thought this would be your best bet, but looking at it more deeply, I think it's only if you want to use a different association name
Specifies the source association name used by has_many :through
queries. Only use it if the name cannot be inferred from the
association. has_many :subscribers, through: :subscriptions will look
for either :subscribers or :subscriber on Subscription, unless a
:source is given.
Being honest, this is one of those questions which needs some thought
Firstly, how are you storing / calling the community_ids array? Is it stored in the db directly, or is it accessed through the ActiveRecord method Model.association_ids?
Looking forward to helping you further!
You don't show the model and relationship definitions for for Community or CommunityPost so make sure you have a has_many :community_posts and a has_many :posts, through: :community_posts on your Community model and a belongs_to :community and a belongs_to :post on CommunityPost. If you don't need to track anything on ComunityPost you could just use a has_and_belongs_to_many :posts on Community and a join table communities_posts containing just the foreign keys community_id and post_id.
Assuming you have the relationships setup as I describe you should be able to just use current_user.posts to get a relation that can be further chained and which returns all posts for all communities the user is associated with when you call .all on it. Any class methods (such as scope methods) defined your Post model will also be callable from that relation so that is where you should put your refinements unless those refinements pertain to the Community or CommunityPost in which case you would put them on the Community or CommunityPost models respectively. Its actually rare to need an AR relationship extension for scopes since usually you also want to be able to refine the model independently of whatever related model you may use to get to it.

Rails associations with self joining class

I doing a rails project where I'm using a self joining one-to-many setup.
I have a class called User, which has many customers.
Each customer has many order lists, like this:
class User < ActiveRecord::Base
has_many :customers, class_name: 'User', foreign_key: 'owner_id'
belongs_to :owner, class_name: 'User'
has_many :order_lists, dependent: :destroy
Now when I opened my rails console. I tried to do this:
user.customers.order_lists
I got this:
NoMethodError: undefined method `order_lists'
While when I try to do this:
user.customers.first.order_lists
I do receive the order lists of that customer.
But how could I recieve all the orderlists of all my customers?
Anybody an idea?
So you want all order_lists for all customers associated with a user?
This will do the trick:
user.customers.map(&:order_lists).flatten
Or you could make a scope on OrderList that takes a set of customer_ids like so:
scope :for_customer_ids, ->(customer_ids) {
where("user_id IN (?)", customer_ids)
}
And then
OrderList.for_customer_ids(user.customer_ids)
In a has many relationship when you call:
user.customers
it returns an array of all the customers the user "has", so when you are calling
user.customers.order_lists
order_lists is being called on an array of customers. (which doesn't have the method order_lists). The first method gives you the first customer in the array so it will work. You could also do something like:
user.customers[0].order_lists
if you wanted. (using first is better though)
Getting the order_lists of all customers would be to loop through all customers and get them like that.

user has many :users, or must I use another way for a friend based social network?

I'm creating a little social network in Rails, where people can add eachother as a friend. I have created a model called 'user', which contains, e-mail, strong md5 hash with salt of password etc.
How do I create something like an option to add another user as a friend? Is it possible to have something like has_many_and_belongs_to :user in the user model? So a user has many users and belongs to many users. Or should I use another way, like adding a friendship model which has user1s_id:integer and user2s_id:integer?
Essentially you want a join model that joins users to users. But you'll have to use more descriptive terms for Rails.
Twitter doesn't use Rails, but here's how their user associations might work in Rails using Twitter's terminology. Note: Twitter doesn't require bidirectional following (ie just because a user follows another, doesn't mean that second user follows the first)
class User < ActiveRecord::Base
has_many :followings
has_many :followers, :through => :followings, :class_name => "User"
has_many :followees, :through => :followings, :class_name => "User"
end
class Following < ActiveRecord::Base
# fields: follower_id followee_id (person being followed)
belongs_to :follower, :class_name => "User"
belongs_to :followee, :class_name => "User"
end
Forced bidirectional friendship (like Facebook enforces that I cannot be friends with you, unless you are a friends with me) will take a little more work. You will either need to manage reciprocal entries with callbacks, or use custom finder SQL. Using custom finder SQL means that ActiveRecord probably won't be able to manage associations for you.
I would suggest that a user has many relationships, this would leave you free to add new types of relationships in the future.
So you start with a "one user to another user" relationship table (minimally, just two IDs) and then in the future you could add a "one user to a group" relationship table (once you've created groups of course!)
I'd suggest using the friendship model because db-wise you'll need a join table either way, but making that table explicit will allow you to store more details about the relationship (e.g. "friend" or "family", date added, ...)

Resources