After reading RailsGuides, some answers here and other docs I am still confused about how to model this relationship.
Thinking of a model where there are objects User and Group, where users can be ordinary members of a group or have management privilege (create group, invite user, remove user, remove group) I want both objects to have two many-many relationships with each other.
user.memberOfGroups = a list of groups the user belongs to
group.userMembers = a list of all users who are members of the group
user.managedGroups = a list of groups the user has management
privileges for
group.managingUsers = a list of users with management privilege for
the group
Is this going to work?
class User < ActiveRecord::Base
has_many :managedGroups, :class_name => "Group", :foreign_key => "managingUsers"
has_many :memberOfGroups, :class_name => "Group", :foreign_key => "userMembers"
end
class Group < ActiveRecord::Base
has_many :managingUsers, :class_name => "User", :foreign_key => "managedGroups"
has_many :userMembers, :class_name => "User", :foreign_key => "memberOfGroups"
end
I have found in a guide how to use scaffold to generate the model objects which is fine with me, I just want to create a basic web service for POST and GET. If the above is correct it should be as simple as creating the model with scaffold and then editing to include the lines shown?
Some issues:
Membership and access control should be modeled by different tables.
Does management of a group imply membership?
The foreign key design as described would only allow for each user to manage one group/one group to have one manager. In all many:many relationships you'll need a third join model.
This would be a good starting point:
class User < ActiveRecord::Base
has_many :users_groups
has_many :groups, :through => :users_groups
end
class Group < ActiveRecord::Base
has_many :users_groups
has_many :users, :through => :users_groups
end
class UsersGroup < ActiveRecord::Base # will be the users_groups table
belongs_to :user
belongs_to :group
end
class Permission < ActiveRecord::Base
belongs_to :user
belongs_to :group
validate_presence_of :name # The name of the permission e.g. "manage"
end
Related
I have a problem understanding the has_many and through relationship. I tried looking at a few other post here was not really clear.(Not sure if its not really because of what I am trying to achieve..)
I am trying to build and app where users have events in a calendar and each user can exchange events among each other.
I want to be able to retrieve data for each user with an API such as "user.trades" <-- which retrieve all trades that the user has made and the other methods, one "user.requested_trades" and the other "user.pending_trades". The requested_trades and pending_trades works but If I should make a 3 way table relations or this 2 way is good enough. Truth fully I really had to play around with the console to figure out what type of query it executes to be able to achieve the two methods to work. But its still very unclear the :through and :has_many.
class Trade < ActiveRecord::Base
belongs_to :seller,
:class_name => "User",
:foreign_key => "seller_id"
belongs_to :buyer,
:class_name => "User",
:foreign_key => "buyer_id"
end
class User < ActiveRecord::Base
has_many :events, :dependent => :destroy
has_many :requested_trades, -> { where(["trades.status = ?",'requested']).order("trades.created_at DESC") },
:class_name => "Trade",
:foreign_key => "buyer_id"
has_many :pending_trades, -> { where(["trades.status = ?",'pending']).order("trades.created_at DESC") },
:class_name => "Trade",
:foreign_key => "buyer_id"
has_many :sent_messages, -> { where(["messages.sender_deleted = ?", false]).order("messages.created_at DESC")},
:class_name => "Message",
:primary_key => "email",
:foreign_key => "sender_id"
has_many :received_messages, -> { where(["messages.recepient_deleted = ?", false]).order("messages.created_at DESC")},
:class_name => "Message",
:primary_key => "email",
:foreign_key => "recepient_id"
has_many :through is many to many relationship between two models. But this many to many relationship maintained through third model.
Suppose, two models teachers and departments. both are bi-directional(may-to-many relation).
But we can't able to maintain the relationship using only these two models so we need third model. Suppose, teacher_departments.
So as any department relation with teacher we can do using the making entry in 3rd model also vice versa.
class Teacher < ActiveRecord::Base
#It describes that teacher many-to many relationship with teacher_department model and
#also if teacher gets deleted depending entries in teacher_departments also gets deleted.
has_many :teacher_departments, dependent: :destroy
#It describe that teacher having multiple departments though 3rd teacher_departments
#model.
has_many :departments, through: :teacher_departments
end
class Department < ActiveRecord::Base
#It describes that many-to many relationship with teacher_department
#model and also if department gets deleted depending entries in teacher_departments
#also gets deleted.
has_many :teacher_departments, dependent: :destroy
#It describe that department having multiple teachers though 3rd teacher_departments
#model.
has_many :teachers, through: :teacher_departments
end
class TeacherDepartment < ActiveRecord::Base
#It describe that this model are belonging to both teacher and department model.
belongs_to :teacher
belongs_to :department
end
Example.
Teacher.first.departments.create()
It will create new entry in teacher_department table with teacher first id and newly created department id.
So you and access departments of first teacher by
Teacher.first.departments
You also take look at following link of Rails Guide
http://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many
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.
My current Group model:
class Group < ActiveRecord::Base
has_many :memberships, :dependent => :destroy
has_many :users, :through => :memberships
end
My Current User Model
class User < ActiveRecord::Base
has_and_belongs_to_many :roles
has_many :memberships, :dependent => :destroy
has_many :groups, :through => :memberships
#some more stuff
end
Membership Model
class Membership < ActiveRecord::Base
attr_accessible :user_id, :group_id
belongs_to :user
belongs_to :group
end
Role Model
class Role < ActiveRecord::Base
has_and_belongs_to_many :users
end
I have a Ability class and CanCan installed to handle roles. I have a role type groupleader and need to make sure a Group has only one groupleader...
I think its something like: Group has_one User.role :groupleader... but I know thats not it.
It doesn't make sense to me to have the role on the users table if you want it to determine what the user can do within the context of a group.
Where it would make sense is to have it on the memberships table for groups and users. Records in this table would then have three columns: user_id, group_id and role.
Then to retrieve the leader for the group you would execute a query like this:
group.users.where("memberships.role = 'leader'").first
Where group is a Group object, i.e. Group.first or Group.find(13).
This then leaves open the possibility that you can have more than one leader for a group further down the track if required.
If your roles are in a separate table, then you can do this:
group.users.where("memberships.role_id = ?", Role.find_by_name("leader").id).first
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.
In the application I'm building, I want to have Events. People can then register for the event by creating a team, by directly registering for an event (no team), or by joining an existing team. I originally set it up as Event has_many teams, Team has_many users, user belongs_to Team and Event. However, I foresee problems if a registered user wants to join another event (or team for another event), and also when trying to list things like #user.events or #user.teams in the control panel. Someone suggested using a join table and creating a Registration model/migration to join these. Can I have a join model that belongs to Event, Team, and User? Am I approaching this all wrong?
You can create a the join model Registration, and make the belongs_to polymorphic so that it can refer to any type of object. Here's an example:
models/team.rb:
class Team < ActiveRecord::Base
has_many :registrations, :as => :owner
end
models/user.rb
class User < ActiveRecord::Base
has_many :registrations, :as => :owner
end
models/registration.rb
class Registration < ActiveRecord::Base
belongs_to :owner, :polymorphic => true
end
models/event.rb
class Event < ActiveRecord::Base
has_many :registrations
end
db/migrations/1234_add_registrations.rb
...
t.belongs_to :owner, :polymorphic => true
t.belongs_to :event
...
Now you can look at look into registrations to see who has signed up for your event. You can use has_many :events, :through => :registrations, :source => :owner to get back to users, but note that there are some limitations, like has_many :owners, :through => :registrations, that are addressed in plugins like has_many_polymorphs.