How to use Includes with activerecord associations? - ruby-on-rails

given two models:
User
has_many :group_members
has_many :groups, :through => :group_members
GroupMember
belongs_to :group
belongs_to :user
Group
has_many :group_members
has_many :users, :through => :group_members
has_many :group_invites
GroupInvite
belongs_to :user
Thanks to Rails, I can easily get all of a user's groups with:
groups = current_user.groups
I then take this list of groups and iterate through to create a JSON object like so:
groups.each do |group|
....
group.group_members.count
group.invites.first_code
etc etc . The problem is this results in a large number of queries because I haven't found a way to use includes(:) to query for all the group_members and invites for all the groups with one query versus a query for every each loop. Any suggestions on how you can use includes like so:
current_user.groups.includes(:group_members, :invites) ?

Try this:
GroupMember
belongs_to :group
belongs_to :user
Group
has_many :group_members
has_many :users, :through => :group_members
has_many :invites, :
has_many :invitees, :through => :invites, :source => :user
Invite
belongs_to :group
belongs_to :user
Now you can:
current_user.groups.includes(:invitees)

Related

search query associated models

I am implementing groups feature. I am stuck at searching members of a group.
Let say
Class Group
has_many :group_memberships
has_many :members, :source => :user, :through => :group_memberships
end
Class GroupMembership
belongs_to :group
belongs_to :user
end
Class User
has_many :group_memberships
has_many :groups, :through => :group_memberships
has_one :profile, :dependent => :destroy
end
Class Profile
belongs_to :user
end
How to search group members, with searchable fields in profile
Could something like below work for you?
group = Group.find(id)
users = group.users.joins(:profiles).where("profile.age>18")
You can do this with the help of eager loading -
just Specify Condition on Eager Loaded Associations like -
group = Group.last
users = group.users.includes(:profiles).where("profiles.name" => "xyz")
For more details refer -
http://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-eager-loaded-associations

How do I collect all the topics that are owned by the members of the groups I am a member of?

This may be tough for me to explain, so if it's not clear just let me know so I can edit as needed!
I have the following example:
class User < ActiveRecord::Base
has_many :topics
has_many :memberships
end
class Topic < ActiveRecord::Base
belongs_to :user
end
#join model between User and Group
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class Group < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :source => :user
has_many :topics, :through => :members
end
The problem I'm having is that I am trying to create a feed (#feed_topics) of all topics that are owned by all the members of the groups I am a member of, and I'm driving myself a little nuts.
Should I try to make this happen using associations, or make an instance method in my User model that has some ActiveRecord/SQL to union all the groups' members' topics into one ActiveRecord::Relation object?
My goal is to write current_user.feed_topics in my controller's action.
Sorry for not explaining earlier! The idea was to utilize 'Nested has_many_through's in order to get to your feed topics. This concept is documented here under the heading 'Nested Associations': http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html.
Let me know if this still is unclear (or if it doesn't work).
class User < ActiveRecord::Base
has_many :topics
has_many :memberships
has_many :groups, :through => :membership
has_many :group_members, :through => :groups, :source => :member
has_many :feed_topics, :through => :group_members, :source => :topic
end
So far these are the final versions of the models from the original question (topic and membership did not change):
class User < ActiveRecord::Base
has_many :topics
has_many :memberships
has_many :groups, :through => :memberships
has_many :feed_topics, :through => :groups, :source => :member_topics
end
class Group < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :source => :user
has_many :member_topics, :through => :members, :source => :topics
end
I am testing right now by adding more groups and members to see if it pulls in all the other members' topics of other groups.
EDIT: things seem to be working ok.
EDIT2: one little problem I had was seeing duplicate topics because a member was in multiple groups. I learned about :uniq => true and it saved the day.

How do you model "Likes" in rails?

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 :)

What would be a good way to model a group in Ruby on Rails

I've started learning Rails recently and have completed the sample app at http://ruby.railstutorial.org. I am trying to add user-created groups to the sample social network. I've gotten stuck at how exactly I should model the group. I know there should be a user_id of who owns the group and there should be strings of the name and description. Aside from that I have no idea what the rest of the group data model should look like. Thank you in advance.
If you want a user to be in multiple groups your relations should be something like this
class Group < ActiveRecord::Base
belongs_to :owner, :class_name => "User"
has_many :memberships
has_many :members, :through => :memberships
end
class Membership < ActiveRecord::Base
belongs_to :member, :class_name => "User"
belongs_to :group
end
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, :through => :memberships
has_many :owned_groups, :class_name => "Group", :foreign_key => "owner_id"
end
What about something like this?
class Group < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :group
end

Using ActiveRecord to auto populate nested fields. User.Group.New

user.rb
has_many :groups, :through => :group_members, :conditions => ['banned = ?', false]
group.rb
belongs_to :user
Groups controller:
group = current_user.groups.new(:uuid => group_uuid)
Rails.logger.info for group.inspect is showing group.user_id as nil ? Why is that... Shouldn't the group.user_id be automatically set when I do a current_user.groups.new() ?
Thanks
I think the problem you're running into is that your associations are inconsistent. In user.rb, you have a has_many :through relation, which means that an association between a user and a group would be stored in the table group_members. In group.rb, you have a belongs_to association, which means that the association would be stored on a column on groups called user_id.
You would usually use a has_many :through type association when users can belong to multiple groups, and groups can contain multiple users (a many-to-many association). belongs_to type associations should be used if a group can only have one user in it, or a user can only belong to one group.
I'm assuming that you want users to belong to multiple groups, and groups to have multiple users. If that's the case, you can fix up your associations by adding the following to group.rb
has_many :group_members
has_many :users, :through => :group_members
group.rb
belongs_to :owner, :class => 'User', :foreign_key => :user_id
has_many :group_members
has_many :users, :through => :group_members
After create add owner as group member
user.rb
has_many :own_groups, :class => 'Group', # own groups
has_many :group_members
has_many :groups, :through => :group_members
group_member.rb
belongs_to :group
belongs_to :user
group = current_user.groups.new(:uuid => group_uuid)

Resources