I have a complex active record relationship in mind, and was wondering how exactly I would accomplish it. In essence:
A User can create a Board. A user can then add other users to that board for collaborative editing. Added users form a Team. Each added User has a Role in each board, (Admin, Editor, Viewer).
I was thinking of setting it up this way:
A User has many Boards
A Board has many Users through Teams
A Team has many Users?
The role part has been confusing me. Do I have to set up a TeamUser model which has many Roles, or does each User have many Roles which belong to a Board? Does a Role even have to be it's own model?
What is the most effective way to accomplish my goal?
If a user could belong to many teams and could have many roles, logic could be something like:
You need a join table to supply the N-to-N relationship between User and Board model. But the thing is, when you add a user to a board, you give them a role in that participation. It's a good place to hold role info in that participation. So our UsersBoards join-table transform into the Participation model (Or whatever you like to call it). Our participation model now holds the user, board and role info (All of them is foreign-key).
I didn't exactly understand the purpose of the Team model but I think it's easy to add a user to a team when it's necessary. It will not hurt to our logic.
Also, if you don't plan to add more features in the future, role can be an enum too.
class User < ApplicationRecord
has_many :participations
has_many :boards, through: :participations
end
class Board < ApplicationRecord
has_many :participations
has_many :users, through: :participations
end
class Participation < ApplicationRecord
belongs_to :user
belongs_to :board
enum role: [ :admin, :editor, :viewer ]
end
user = User.first
user.participations.first.board #Board
user.participations.first.admin? #true
user.boards
And you can add helper methods or delegate to models for shortening the query.
Related
I have a pretty standard User/Role setup going on (a user HABTM roles, a role HABTM users). I'm using CanCanCan for authorisation, and the role you have defines what you can do around the application. This part is all working fine, but now I want to be able to have users inherit roles as part of having a subscription to different memberships.
Here are the models concerned:
class User < ApplicationRecord
has_and_belongs_to_many :roles
has_one :membership_subscription
has_one :membership, through: :membership_subscription
end
class Role < ApplicationRecord
has_and_belongs_to_many :users
end
class MembershipSubscription < ApplicationRecord
belongs_to :user
belongs_to :membership
end
class Membership < ApplicationRecord
has_many :membership_subscriptions
has_many :users, through: :membership_subscriptions
end
I was thinking that I might be able to just add a has_many: roles association to the Membership, and then say that the user has_many roles through their subscription to the Membership, as well as the current HABTM association that allows roles to be assigned directly.
This way you can directly attach roles to users like you can now (as some roles are additive, and not related to the membership subscription/type at all) but also users will automatically inherit roles (and lose them again) as their membership subs come and go.
Does that make sense? I guess the other option would be to use callbacks in the model to deal with creating/deleting role associations but it doesn't seem as elegant.
Any advice greatly appreciated!
Okay so I think this is a valid answer:
First, update the models so that there is an associations between the Memberships and the Roles:
class Role < ApplicationRecord
has_and_belongs_to_many :users
has_and_belongs_to_many :memberships
end
class Membership < ApplicationRecord
has_many :membership_subscriptions
has_many :users, through: :membership_subscriptions
has_and_belongs_to_many :roles
end
Next, create a method in the user model that can be used to retrieve both directly assigned roles and inherited roles:
def combined_roles
if self.membership == nil
self.roles
else
self.roles + self.membership.roles
end
end
Then wherever you need to check a role, call that method instead of the usual user.roles
Not sure if that's a naive way of doing things, but seems to work okay. Comments still welcome if there's a better way
EDIT:
This allows a user to have the same role multiple times - it can be assigned directly or inherited. Modify the combined_roles method like so so that it strips out duplicates:
def combined_roles
if self.membership == nil
self.roles
else
(self.roles + self.membership.roles).uniq
end
end
I'm setting up an app that has Users and Brands. Most users will not be associated with a brand, and will only be able to comment on Brand pages. Some users, however, will be associated with a single brand. I want these users to be the "admins" or owners of this brand. E.g. Users A and B are both "admins" of a brand, and so can create/edit/update the brand, etc. My question is, how should I set up the Brand resource such that it "belongs_to" multiple users? I understand that I could say brands have_many users, but is it weird to say that an object "has" a user? Is it more appropriate to "belong" to users? This description leads me to believe so: "For example, it makes more sense to say that a supplier owns an account than that an account owns a supplier."
It's definitely a has_many relationship.
But it may be clearer to refer to those special users as 'administrators' or 'admins'
class Brand
has_many :administrators, class_name: 'User'
end
If it turns out that a user can be administrator for several brands, then you'll need a join table, either HABTM or HMT. HMT is the better choice in case you want to store characteristics about the join (e.g. when he became administrator)
class BrandUser
belongs_to :user
belongs_to :brand
end
class Brand
has_many :brand_users
has_many :administrators, through: :brand_users, source: :user
end
class User
has_many :brand_users
has_many :brands, through: :brand_users
end
I'm trying to set up models in such a way that Users can create Lessons and then other users can sign up for them.
Right now my models are set up like this:
class Lesson < ApplicationRecord
belongs_to :teacher, class_name: 'User'
has_many :students, class_name: 'User'
end
class User < ApplicationRecord
has_many :lessons
has_many :students, :through => :lessons
end
I want to be able to access the users signed up for a lesson by #lesson.students for example. I'd also like to be able to get all the lessons that a student is participating in (can't really see how I'd do this with my current set up).
Are my model associations right for how I'd like to use them? If so, how can I create the migrations to add the necessary references to my database models?
If you want the ability to create nested resources from it's parents then you have to add:
accepts_nested_attributes_for
to the parent model.
Also, I recommend you to read how to set up has_many through relationships, you need a join model for rails to do its magic and link the 2 models
Once you set everything up, create the join model (with it's respective foreign keys, one for lesson and the other for user) rails will take care of the associations between the models, allowing you to do things like:
User.last.lessons #lessons created by the last user
and
Lesson.first.users #users subscribed to a lesson, in this case the first one
I'm building a simple rails app that allows users to login and create private lists for themselves, so far so good. I would like the user to then have the option to invite people to a certain list where these new users would have edit access to the tasks. This would be in the same vein as basecamp and trello in regards to adding users.
Where would I begin to start with this I have been wracking my brains and searching google and can't find any relevant tutorials of guidance.
Ok, so what about having
1) A users Table (model)
2) A lists table (model)
Model Associations
A user has many lists
A list belongs to a user
User table will have Foreign Key list_id (you will need to add this via a migration)
Thats just a start, I am assuming you know about resources ( the big 7 ) in rails? As you will be using this extensively
You can create a Membership table which will act as a join table between a User and List. You can then add an :admin attribute to the Membership model with a boolean type.
class List < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships
end
class User < ActiveRecord::Base
has_many :memberships
has_many :lists, :through => :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :list
end
I have a group model and a user model. This is an m to n relationship. The group and user models are joined through a separate membership model.
Some subset of the users that belong to a group are administrators. When a new user tries to join a group by creating a new membership, I would like this action to be confirmed by one of the group administrators before the new user can actually participate in the group.
What is the best way of accomplishing this? My current idea is to have a separate model called permission or invitation that only the group admins can create. A user is only a member of the group when they are part of both a membership and an invitation. This seems like a lot of models for a fairly commonplace task. If this is actually the best approach, some advice on how to go about doing it would be greatly appreciated.
I don't want to use email for confirmation.
I am using Rails 3.1.0.
Why not include the confirmation status in the join model itself? So let's assume you have a User, a Group model and they are joined by a Membership model. Add an attribute "accepted_on" with format DateTime to the Membership model (or just a boolean if you don't care when exactly they were accepted.) Then make a view that lists all the memberships that's only visible to the admins with a link that only they can access which sets the "accepted_on" attribute to Time.current.
In your User/Group model you can then do the following:
class User < ActiveRecord::Base
has_many :memberships, :conditions => 'accepted_on IS NOT NULL'
has_many :groups, :through => :memberships
end
class Group < ActiveRecord::Base
has_many :memberships, :conditions => 'accepted_on IS NOT NULL'
has_many :users, :through => :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
This way when a new user adds a membership it won't become available until the admin has accepted him (that is, made accepted_on anything other than nil.)