I have four models, User, Team, Idea and Member. Member is join table between Team and User. I want users to be able to visits each others profile pages (controller: :users, action: :show), in the show view I want to list Ideas that belongs to teams that both users are members of. I've created a related question for listing the correct teams here.
Here is what I have now:
def show
#user = User.find(params[:id])
# Only list teams that both users are members of
#teams = #user.teams.find(#user.members.pluck(:team_id) & current_user.members.pluck(:team_id))
# What should I do to list ideas that are created for teams the both users are members of?
#ideas = #user.ideas.search(params[:search], conditions: { 'how should the condition look?' } :page => params[:page], :per_page => 10, :order => 'created_at DESC')
end
My models is set up like this:
have two models, User and Team, it's a many-to-many with a join table called Member. It's set ut like this:
#Team:
has_many :members, dependent: :destroy
has_many :users, through: :members
#User
has_many :members
has_many :teams, through: :members
#Member
belongs_to :user
belongs_to :team
#Idea
belongs_to :user
belongs_to :team
Any ideas on what I should do?
You'll want the following attribute in your Idea index (if you haven't already):
has team_id
And then the following should do the trick (given the existing context of your controller):
#ideas = Idea.search params[:search],
:with => {:team_id => #teams.collect(&:id)},
:page => params[:page],
:per_page => 10,
:order => 'created_at DESC'
You do not want to direct the idea search through #user.ideas - that will limit it to only ideas attached to #user and their corresponding teams, not current_user's teams.
Related
I have the following association in my Group model:
has_many :subscriptions, :order => 'subscriptions.expiration_date DESC', :group => 'subscriptions.product_name', :conditions => ['subscriptions.status IN (?)', [:active, :canceled]]
The Models (User, Subscription, Group) break down like this:
**User**
has_many :groups, :before_add => :validates_group, :through => :group_users, :conditions => ["groups_users.status = ?", 'active']
**Group**
has_many :users, :through => :group_users, :conditions => ['groups_users.status = ?', 'active']
has_many :subscriptions, :order => 'subscriptions.expiration_date DESC', :group => 'subscriptions.product_name', :conditions => ['subscriptions.status IN (?)', [:active, :canceled]]
**Subscription**
belongs_to :group
So, a group could have multiple subscriptions (for different products; subscription.product_name) for the same product (depending on if they renewed or it was just an old one in the system). The has_many :subscriptions order_by is not working properly.
For Example:
If the user has 3 subscriptions (One for product X and 2 for product Y). And you run the following
Subscriptions Method in User Model:
def subscriptions
subs = []
groups.collect(&:subscriptions).each do |sub|
if sub
subs << sub
end
end
subs.flatten!
end
u = User.last
u.subscriptions
The one for product X comes back fine, but when it pulls back product Y it is pulling back the first one in the list (the one with the expiration date in the past).
Sorry if this doesn't make sense, I am trying to explain it the best I can. Anyone run into this situation where order and group are not working properly on an association?
In my User model I have the following:
class User < ActiveRecord::Base
has_many :friendships, dependent: :destroy
has_many :friends, :class_name => "User", :foreign_key => "friend_id"
has_many :pending_friends,
:through => :friendships,
:conditions => "status = 'pending'",
:foreign_key => "user_id",
:source => :friend
has_many :requested_friends,
:through => :friendships,
:source => :friend,
:conditions => "status = 'requested'"
def friends
direct_friends | inverse_friends
end
In my Friendship model I have the following:
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => "User", :foreign_key => "friend_id"
In my view I have created an array of each of the users' friends, shown below. This code works and populates the array with all the data from the friends' "User" database model attributes.
User.first.friends
However, I want to be able to call the users' friend's name's. So, for example, shouldn't I be able to do something like this?
User.first.friends.map(&:name)
How do I get an array containing just the friend's name's, instead of all the friend's user attributes? I would also appreciate if anyone could tell me why .first is used (I got that from here: Rails calling User record from Friends model), as it doesn't just get the first instance of the User's friends (it gets all the instances). And why does just doing:
User.friends
return an empty array?
Try method pluck:
User.first.friends.pluck(:name)
You should use first method to retrieve one object from table. The User is table with a lot of users.
I'm working on an app that has the models User and Project, and User can be assigned to multiple Projects, via ProjectUser, with a role (e.g. Developer, Designer).
Project
has_many :project_users
has_many :users, :through => :project_users
User
has_many :project_users
has_many :projects, :through => :project_users
ProjectUser (user_id, project_id, role)
belongs_to :user
belongs_to :project
I can call #project.users and #user.projects, but since there are varying roles, I'd like to be a bit more specific with the relations. Ideally, I want to be able to do the following:
#project.developers
# returns #project.users, but only where ProjectUser.role = 'Developer'
#project.designers << #user
# creates a ProjectUser for #project, #user with role 'Designer'
#user.development_projects
# returns projects where #user is assigned as a 'Developer'
#user.design_projects << #project
# creates a ProjectUser for #project, #user with role 'Designer'
I currently have the following code:
has_many :developers, :through => :project_users, :source => :user,
:class_name => "User",
:conditions => ['project_users.role = ?','Developer']
But this only really does the fetching one-way, and doesn't give me much else - I can't build or assign or anything.
I'm attempting some more complex logic which I think might work, but would appreciate some pointers:
has_many :developer_assignments, :source => :project_user,
:conditions => { :role => 'Developer' }
has_many :developers, :through => :developer_assignments # class_name?
Any suggestions? Thanks!
has_many accepts a block that can define/override methods for the association. This will allow you to create a custom method for <<. I've created a small example for you, you could create build in a similar fashion.
# Project.rb
has_many :developers, :through => :project_users, :source => :user,
:conditions => "project_users.role = 'developer'" do
def <<(developer)
proxy_owner.project_users.create(:role => 'developer', :user => developer)
end
end
Now you can add a new developer to your your project with: #project.developers << #user as requested. #project.developers gives you all the developers.
If you have a lot of roles, it might be useful to create these has_many statements dynamically.
# Project.rb
ROLES = ['developer','contractor']
ROLES.each do |role|
self.class_eval <<-eos
has_many :#{role.downcase}s, :through => :project_users, :source => :user,
:conditions => "project_users.role = '#{role}'" do
def <<(user)
proxy_owner.project_users.create(:role => '#{role}', :user => user)
end
end
eos
end
Looking back at everything above it doesn't seem like the rails way of doing things. Scoping this should make it possible to get the build and create commands working without redefining everything.
Hope this helps!
It sounds like what you're looking for is a combination of RoR's single table inheritance and named scopes.
Take a look at the following article for a nice example about polymorphic associations. This should help you with achieving the following:
#project.developers
# returns #project.users, but only where ProjectUser.role = 'Developer'
#project.designers << #user
# creates a ProjectUser for #project, #user with role 'Designer'
Scopes will give you a clean way to implement #user.development_projects but there may be more trickery required to get the << operator.
Did you try using scopes yet? It doesn't let you do <<. But it simplifies querying.
Try:
Project
scope :developers, lambda {
includes(:project_users).where("project_users.role = ?", "developer")
}
You will be able to get all developers using: #project.developers
I've built a simple Friend model, which allows Users to have multiple friends. Here's what that looks like:
class Friend < ActiveRecord::Base
belongs_to :user
class User < ActiveRecord::Base
has_many :friends
Each friend record just has an id, user_id and friend_id. The user_id is the id of the user it belongs to and the friend_id is the id of user they are befriending.
Here's my problem
I'm not quite sure how to display a list of a particular user's friends. #user.friends will give me a list of all the friend records they have, but not the user accounts of those friends.
For instance, I am trying to build a show page for the friends controller:
class FriendsController < ApplicationController
def show
#user = current_user
end
SHOW.HTML.ERB
<% if #user.friends.count > 0 %>
<% #user.friends.each do |friend| %>
<div class="entry">
<%= friend.username %>
This does not work because friend in this case does not have username. I need to do something like this in my controller:
#friend = User.find_by_id(friend.friend_id)
But I'm not sure how I would call that in my view in the #user.friends loop. Any thoughts appreciated. Let me know if I need to be more clear.
UPDATE
I've updated my User model like so:
has_many :friends, :include => :user
has_many :friended_users, :through => :friends, :source => :user, :uniq => true
However, when I run #user.friended_users it's giving me the user_ids (which is the same as #user) rather than friend_ids.
How can I tweak that relationship so it's linking to the friend_id rather than user_id?
The more I think about it, I think I may not have set up the relationship properly in the first place. Maybe a User should has_many :users, through => 'friends', but that doesn't really make sense...
UPDATE
I've updated my models based on #twooface's input:
class User < ActiveRecord::Base
has_many :friendships
has_many :friends, :through => :friendships
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => 'User'
class Friend < ActiveRecord::Base
has_many :friendships
has_many :users
I'm just not sure what my Friends table should look like. I assume it should have a primary key and a user_id? If I create a friendship and friend record, I can do friendship.user and friendship.friend and get the correct results, but user.friends gives me an empty hash...
I think your relations are built a bit wrong. Try something like this:
class User < ActiveRecord::Base
has_many :friends
has_many :friendships
has_many :friends, :through => :friendships
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => 'User'
# This class has :user_id and :friend_id
Then everything will be simpler. Every user will have an array of friends that will be just users.
User.first.friends
Will return an array of Users that this User friends.
Hope this helps.
I've got 3 models: User, Team, and Membership -
class Team < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :source => :user
end
class User < ActiveRecord::Base
has_many :memberships, :dependent => :destroy
has_many :teams, :through => :memberships
def team_mates
teams = Team.where(:members => id)
team_mates = teams.members
end
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :team
validates :user_id, :presence => true
validates :team_id, :presence => true
end
And, I can't quite figure out how to write the team_mates method in the User model. It should return an array of the other users that are in a team with the current_user. My thought is that I should be useing a scope to limit Team to only include teams where the current user is a member but I can't quite figure out the syntax. Any help on this would be greatly appreciated.
Thanks!
Use the membership table to find users who share any team with the user you are calling the method on. Then to filter out duplicate users who share more than 1 team with current user, use distinct.
I haven't tested the below code, but hopefully it will get you on the right path:
def team_mates
m = Membership.scoped.table
Users.join(m).where(m[:team_id].in(team_ids)).project('distinct users.*')
end
UPDATE
Looks like some of the Arel methods in that answer don't have an easy mapping back to ActiveRecord land. (If someone knows how, I'd love to know!) And also it returns the 'current user'. Try this instead:
def team_mates
User.joins(:memberships).where('memberships.team_id' => team_ids).where(['users.id != ?', self.id]).select('distinct users.*')
end
How about?
User.joins(:memberships).joins(:teams).where("teams.id" => id).uniq
Maybe something like this
scope team_mates, lambda {|user_id, teams| joins(:memberships).where(:team_id => teams, :user_id => user_id)}
def team_mates
User.team_mates(self.id, self.teams.collect {|t| t.id})
end
Here is more info:
http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html