I have several models that are relevant to this question..
UserGroupPermission
UserGroup
User
UserPermission
Permission
Essentially, A User can be assigned a permission through either their UserGroup or directly. UserGroups are tied to specific permissions via the UserGroupPermission model. Users are tied to specific permissions via the UserPermission model.
The end result is to be able to call User.permissions and return every permission assigned to that user either directly or through a UserGroup.
Here is my current model
class User < ActiveRecord::Base
belongs_to :user_group
#Permissions
has_many :user_permissions
has_many :user_group_permissions, :through => :user_group
has_many :permissions, :through => :user_group_permissions #and :user_permissions
end
I've search high and low for a solution to this, but can't seem to find my exact case. Thanks in advance!
Edit: I've been told that is unclear what I'm asking. Hopefully this will help: Basically the User has_many :permissions, :through => :user_group_permissions and :user_permissions, but you can't use an and to join the results together. I'm trying to retrieve and combine the results of both of those relations into one.
Edit 2: I've been doing some work. This code gives the desired result, but I'm not sure how well it would work for potentially large databases.
#Permissions
has_many :user_permissions
has_many :user_group_permissions, :through => :user_group
#has_many :permissions, :through => :user_group_permissions
#has_many :permissions, :through => :user_permissions
def permissions
all_permissions = []
UserGroupPermission.where('user_group_id = ?', user_group_id).each do |ugp|
all_permissions.append(Permission.find(ugp.permission_id))
end
UserPermission.where('user_id = ?', id).each do |p|
all_permissions.append(Permission.find(p.permission_id))
end
return all_permissions
end
Related
I have a model of users and things
Users can have one leader and many followers which are other users. User can also have things
So I have these definitions which use a self join relationship:
class User < ActiveRecord::Base
has_many :things, dependent: :destroy
has_many :followers, :class_name => 'User', :foreign_key => 'leader_id'
belongs_to :leader, :class_name => 'User', :foreign_key => 'leader_id'
end
class Thing < ActiveRecord::Base
belongs_to :user
end
So I can query ask for a list of things that a user has by asking, for example User.first.things. I can also get a list of followers of a user with User.first.followers.
How do I get a list of things that a user's followers have. I think I might need to use a has_many through relationship but I can't seem to figure it out as I'm not sure how to deal with the fact that a Leader can have things through a 'follower' but also directly themselves
Thanks
Something like this:
def all_things
Thing.where(:user_id => followers.map(&:id).push(id))
end
It returns a scope so you should be able to continue the chain, for example:
User.first.all_things.visible
Update
If you are only interested in the followers' things without adding the user's things to the batch it is better is you do it directly with a has_many through:
has_many :followers_things, :through => :followers, :source => :things
Check this other SO thread
How about:
all_things = (your_user.followers.each { |f| f.things }).flatten
This should return an array containing things that belong to all your_user's followers.
I'm trying to prevent my users from creating a relationship in a 'has many through' association with a record that doesn't belong to them.
My users have many locations through location_users. And their locations have many shops through location_shops. I have things protected currently with CanCan.
class User < ActiveRecord::Base
has_many :locationusers
has_many :locations, :through => :locationusers
end
class Location < ActiveRecord::Base
has_many :locationusers
has_many :users, :through => :locationusers
has_many :location_shops
has_many :shops, :through => :location_shops
end
class Shop < ActiveRecord::Base
has_many :location_shops
has_many :locations, :through => :location_shops
end
And my cancan abilities
class Ability
can [:manage], Shop, { :locationusers => {:user_id => user.id }}
can [:manage], Location, { :locationusers => {:user_id => user.id }}
end
I can handle the creation / editing of locations via this setup and my users can only view / edit their own locations / shops.
The issue is the creation of these relationships.
If a user posts a location id which doesn't belong to them, the relationship is created regardless of whether they have permission to create it. Granted, they can't view this relationship but I need to prevent the creation in the first place.
Eg, a user with a single location with ID 314
>> User.last.locations.map(&:id)
=> [314]
When creating a new shop, if I alter the params posted:
:shop=>{:shop_name=>"Ye Old Shoppe", :location_ids => [1,2,3,314]}}
The above creates the relationship for four locations obviously. I need it to validate the location ids before the creation of the relationship.
The only thing I could come up with was adding before_add in the model:
class Location
has_many :location_shops
has_many :shops, :through => :location_shops, :before_add => :check_location_ownership
end
Is this the correct way to go about it and if so, what should :check_location_ownership look like? Or, is there a better way to prevent the creation of the relationship?
Although what you have done does make sense, there are 2 other ways I can think of.
1) Use :conditions option on the has_many relationship.
2) A custom validation method.
class Location
has_many :location_shops
has_many :shops, :through => :location_shops
validate :check_location_ownership
end
I would personally choose one of these 3 depending on the case.
So this might be really bad form. I'm relatively new to rails. I'm not sure.
I have a project model and I want there to be many owners (who can read and write everything) and many collaborators (who can read and write some stuff).
In my project.rb file I have:
has_many :project_user_relationships, :dependent => :destroy
has_many :collaborators, :through => :project_user_relationships, :source => :user
has_many :project_owners_relationships, :class_name => "ProjectUserRelationship", :foreign_key => "project_id",
:before_add => Proc.new { |p,owner_r| owner_r.owner = true }, :conditions => "`project_user_relationships`.owner = true"
has_many :owners, :through => :project_owners_relationships, :source => :user
So this works reasonably well. If I add a new owner, that user is also a collaborator which is what I want. The issue I'm not sure how to solve is if I add a user that is already collaborator as an owner, I get two entries in the join table. I'd like for it to just amend the record that's already there. How do I do that?
Here's the data model I would suggest for this:
class Project < ActiveRecord::Base
has_many :memberships, :dependent => :destroy
...
end
class Membership < ActiveRecord::Base
belongs_to :project
belongs_to :user
...
end
class User < ActiveRecord::Base
has_many :memberships, :dependent => :destroy
has_many :projects, :through => :memberships
...
end
And then the membership table will have the following attributes:
:id
:user_id
:project_id
:is_owner (boolean)
A scope defined on the membership class:
scope :owner, where("is_owner")
And a special method for User instances:
def owned_projects
memberships.owner.includes(:projects).inject([]) {|array, m| array << m.project; array}
end
will allow you to retrieve a user's owned projects with the user.owned_projects call.
And just a call to user.projects to see a user's projects that they either collaborate on or own.
You have better data normalization with this data model, and a simple boolean attribute to define whether or not a user is a project owner.
This data model is used in this project, with the exception that s/Project/Group/, and there's some additional functionality to handle inviting users to the Project.
This doesn't answer your "real question", but I think part of the issue is that a data model where collaborators are owners are stored in the same table is needed to minimize redundancies and the need to manage two separate tables.
I am having a somewhat too nested database layout, however, I seem to need it. That is, Our website visitors may each have a single account with maintaining multiple users (think of identities) within.
Now they may create tickets, which are grouped by ticket sections, and we have ticket manager (operator) to process the incoming tickets.
Not every ticket manager may see every ticket but only those this manager is a member of the given ticket section for.
Now, I am totally fine in querying via raw SQL statements, but I failed to verbalizing those two special queries the Rails way.
Here is the (abstract) model:
# account system
class Account < ActiveRecord::Base
has_many :users
has_many :tickets, :through => :users
has_many :managing_ticket_sections, ... # TicketSection-collection this account (any of its users) is operate for
has_many :managing_tickets, ... # Ticket-collection this account (any of its users) may operate on
end
class User < ActiveRecord::Base
belongs_to :account
has_many :tickets
has_many :managing_ticket_sections, ... # TicketSection-collection this user is operate for
has_many :managing_tickets, ... # Ticket-collection this user may operate on
end
# ticket system
class Ticket < ActiveRecord::Base
belongs_to :author, :class_name => "User"
belongs_to :assignee, :class_name => "User"
belongs_to :section, :class_name => "TicketSection"
end
class TicketSection < ActiveRecord::Base
has_many :tickets
has_many :operators
end
class TicketSectionManager < ActiveRecord::Base
belongs_to :manager, :class_name => "User"
belongs_to :section
end
I am aware of basic has_many :through-constructs, however, here, I am accessing more than three tables to get the tickets.
Something that actually works for in the User's model is:
class User < ActiveRecord::Base
has_many :managing_relations, :class_name => "TicketSectionManager" # XXX some kind of helper, for the two below
has_many :managing_sections, :class_name => "TicketSection", :through => :managing_relations, :source => :section
has_many :managing_tickets, :class_name => "Ticket", :through => :managing_relations, :source => :section
end
Here I am using a helper relation (managing_relations), which is absolutely never used except by the two has_many relations below.
I were not able to describe a User.managing_sections nor User.managing_tickets relation without this helper, which is, where I need an advice for.
Secondly, the customer is to have a look at all of the tickets he can manage on any User (think of an identity) he has logged in, so what I need, is a way to collect all tickets (/sections) this Account is permitted to manage (identified by being a member of the corresponding TicketSection)
Here I even were not able to express this relation the ruby way, and I had to work around it by the following:
class Account
def managing_tickets
Ticket.find_by_sql("SELECT t.* FROM tickets AS t
INNER JOIN ticket_section_managers AS m ON m.section_id = t.section_id
INNER JOIN users AS u ON u.id = m.user_id
WHERE u.account_id = #{id}")
end
end
I'd appreciate any kind of advice, and
many thanks in advance,
Christian Parpart.
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, :through => :memberships
class Group < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships
def moderators
# relationship_id is in the memberships table
self.users.find(:all, :conditions => ['relationship_id = ?', 1])
end
end
Given the above code, is this the best way to implement the moderators method? My goal is to be able to find all the users with a "moderator" relationship within a given group. Right now I can use the method to iterate over all the moderators
# ...
#group.moderators
I considered an association extension which makes sense to use here because I'm asking for a subset of users that meet a condition. But the syntax seems redundant to ask for users that are moderators
# Association extension - seems redundant
#group.users.moderators
I considered a named_scope but I couldn't figure out how to implement it without errors. Even if I could the named_scope will return all the moderators across all the groups which isn't what I want.
# named_scope: returns all the moderators across all the groups
moderators = Group.moderators
I'm wondering what the best practice here is and why I might want to us an association extension (or named_scope) over the instance method given that it allows for a more concise syntax?
Add an association on Group class:
class Group < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships
has_many :moderators, :source => :user, :through => :memberships,
:conditions => ['relationship_id = ?', 1]
end
Now you can do the following:
#group.moderators
#group.moderators.size
#group.moderators.find_by_city(..)