Is it better to have roles as a column on my users table, or do it through join tables (Roles & Assignments)? - Rails 3 - ruby-on-rails-3

You can see my models here:
https://gist.github.com/768947
Just to explain what is happening, I have a few models. At the core of my app, there are: projects, stages, uploads, comments, users. Then there are roles & assignments to manage user authorization.
I am doing this with the plugin declarative_authorization & devise for login.
So the first question is, is it better to just add a column of 'Roles' to my user model/table and store the roles for each user there? If I have a user that has multiple roles, then I can just store all the roles as an array and cycle through them as needed.
Or is it better to do it like I have it setup now, where I use two separate tables and a bunch of joins to setup the assignments? I only have 4 roles: designer, client, admin, superuser.
Better in the sense that it is 'less expensive' from a computing resources standpoint to do each query with the column, than with the joins or is the difference not that significant?
I guess, the root of my question is...right now if I want to get a project assigned to the current_user I simply do current_user.projects.each do |project| and cycle through them that way. This is after I have done: #projects = current_user.projects in the projects controller. The same applies for all my other models - except Users & Roles.
However, if I wanted to find a user with role "client", it becomes convoluted very quickly. Or am I overcomplicating it?
Any help would be appreciated.

I think it's better to have user and role tables that are separate. It's a many-to-many relationship, because a user can have many roles and many users can have the same role. You'll need a JOIN table (e.g. user_role) to do that. I'd recommend three tables. Of course you'll have primary keys for both user and role; the user_role table will have two columns, one for each primary key, and foreign key relationships to their respective tables.
So now if you want all users with the role "client", it's an easy JOIN between user and user_role. If you want a particular user's roles, you'll need to JOIN the three tables.
I would not recommend an array of roles in user. It goes against first normal form.

Related

Best way to mimic inheritance in postgresql?

For an application I am writing, there are two types of "users", those who have made accounts and those who have not, virtual_users. These two types are nearly identical, except account_users have a password, and email is required and must be unique amongst all account_users, although it can be the same as any number for virtual_users. A large number of tables have a column that references users, which should include both, and 90% of app functionality treats them as interchangeable. What is the best way of handling this? Some options I have considered:
-Put both types of users in the same table and have a complicated constraints regarding uniqueness, basically, if password is not NULL, email must be unique among all users where password is not NULL. I have no idea how I would write this constraint. On the few occasions I only want account_users query for only users who have a password. This seems like the best solution if I can figure out how to write the constraint.
-Have Account_users inherit from Virtual_usersand Virtual_users has an additional column password and unique constraints on email. From here there are two potential options:
---Have a Users table which includes two columns account_user_id and virtual_user_id one of which is NULL and one of which corresponds to the appropriate user. When other tables need to reference a user, they reference this table. Have all my queries server side for users query both tables and combine.
---When other tables need to reference they reference either table. I don't think this is possible. Have all my queries server side for users query both tables and combine.
Any advice would be appreciated.
I assume the scenario is you have a system which some parts require the user to be signed into a registered account, and others do not, but you'd still like to track users.
Postgres has table inheritance. You could use that, but I'd be concerned about the caveats.
You could put them all into one table and use some sort of flag, like Single Table Inheritance, but then you run into constraint issues. You would then enforce constraints in the model. This should be fine if you have a strong model.
You could have separate accounts and users. Rather than one being a special case of the other, they key is thinking of them as two conceptually different things. In OO terms, an account has a user.
-- Visitors to the site who haven't signed up.
create table users (
id serial,
-- The unverified email they might have given you.
email text,
-- Any other common information like a tracking token
token text
);
-- Users who have registered.
create table accounts (
id serial,
user_id int references users(id),
-- Their verified email.
email text not null,
-- Hashed, of course.
password text not null
-- any additional information only for accounts
);
I like this because there are no flags involved, users and accounts can have separate constraints, and the accounts table doesn't get bloated with users that showed up once and never came back.
You'd access complete account information with a simple join.
select accounts.*, users.token
from accounts
join users on accounts.user_id = users.id
where accounts.id = ?
If you want to delete an account you can do so without losing the underlying user information.
delete from accounts where accounts.id = ?
Systems which require an account use accounts. Systems which don't use users. Systems which behave differently for users and accounts can check if a user has an account easily.
select accounts.id
from accounts
where accounts.user_id = ?
User tracking is associated with the users table, so you have a consistent record of a user's behavior before and after they register.

Many-to-many hierarchical relationships

In my application I have three main tables:
User
Group
Role
Any combination has a many-to-many relationship.E.g. a user can be in different group, having different roles in each of them.
The easiest part is to map user and group, done by the intermediate table User_Group. Then, when it came to design how to link the three together I had some doubts.
Q: Do I add another column in User_Group? Or create additional intermediate table?
Thinking about the second option, I tried this:
which (I think) would make easier and tidier retrieving the information I need in the front-end:
Groups available for the user (User_Group)
Roles available for a group (Group_Role)
Roles available for a user in a given group (user_Group_Role)
I don't see anything wrong with the schema you have proposed at the end of your post.
It ensures a user can be associated to a group without them being required to have a role within that group.
It can ensure that a user can only take a role applicable to the specific group.
It can allow the same role to be shared across multiple groups.
It can allow a group without any roles
To shrink the schema you can come up with some nice tricks:
Create a role for doesn't have a role in this group; to remove the need of user_group
Create a dummy user for each group, that has every role the group is eligible for; to remove the need of group_role
etc,etc
The down side here is that you end up needing bespoke code to deal with changes, or enforcing constraints. The schema you propose at the end of your post can enforce all the required constraints with foreign key constraints and composite primary key constraints. And will in general be more flexible to future changes.
I see no reason not to use the schema you have proposed, it seems perfectly correct, maintainable, understandable, and resilient to me.
I would go with additional column in existing joining table. You can still fairly easily answer all your questions:
Groups available for the user
select *
from Roles r
join JoiningTableName j on r.Role_Id = j.Role_Id
where j.User_Id = myUserId
Roles available for a group
select *
from Roles r
join JoiningTableName j on r.Role_Id = j.Role_Id
where j.Group_Id = myGroupId
Roles available for a user in a given group
select *
from Roles r
join JoiningTableName j on r.Role_Id = j.Role_Id
where j.User_Id = myUserId and j.Group_Id = myGroupId

1 to 1 database design

I have a database and normally users are the central object.
In database I have a tables roles and users in roles so I can give users different permissions on site.
But now I have one doubt.
I saw before one database with structure like this (User is in 1:1 relation to other tables):
User{UserId, Username, Email etc.}
Admin{UserId, some specific columns}
Child{UserId, some specific columns}
Admin{Parent, some specific columns}
and other tables are connected to user not through User table but through Admin, Child and admin.
Because now I design my database I have Users and user can be Admin, CompanyManager and Professor.
I wonder should I design table like I show above or just relied on roles?
What is advantage of the first approach?
And also in my business model User doesn't have to be CompanyManager or Professor so this is actually 1 to 0 or 1 design.
What is a good way to design database with scenario like this?
The advantage of having specific tables for certain types of users is to store information that is only relevant to that class of user.
In your example,
perhaps all users would have a manager - so the manager ID would go in the users column.
managers would have permissions to view certain information, so this would be handled via a role.
only professors would have a Subject, or the "HasTenure" property. So this information would go in a professors table. Using this approach, you avoid the need to have a "Subject" column in the users table, when it is only applicable to a certain subset of users. As such you avoid a functional dependency in the users table, and a violation of 3rd Normal Form.
I prefer this approach:
In this way, you can easily group Roles into categories and assign them to users.

Database design relations in User and Profile

I'm designing a web application for a school. So far, I'm stuck with the database which has these tables:
users
id
username
password
profile
user_id (FK)
name
last_name
sex
group_id (FK)
(other basic information)
... And other tables irrelevant now, like events, comitees, groups and so on.
So, the users table stores basic information about the login, and the profiles table stores all the personal data about the user.
Now, the *group_id* column in the profile table has a foreign key that references the ID column of the group in which the user is currently enrolled, in the groups table. A user can only be enrolled in one group at once, so there's no need for any additional tables.
The thing is that it doesn't make much sense to me declaring a relation like group HAS MANY profiles. Instead, the relation should be group HAS MANY users, but then, I would have to put a *group_id* column on the users table, which doesn't really fit in, since the users table only stores auth information.
On the other side, I would like to list all the users enrolled in a group using an ORM and getting the a users collection and not profiles. The way I see it, is that the users table is like the 'parent' and the profiles table extends the users table.
The same problem would occur when setting attendances for events. Should I reference the profile as a foreign key in the events_attendance table? Or should I reference the user ID?
Of course both solutions could be implemented and work, but which of them is the best choice?
I have dug a little and found that both solutions would comply with 3NF, so in theory, would be correct, but I'm having a hard time designing the right way my database.
This is a question of your own conventions. You need to decide what is the main entity, right after that you can easiy find a proper solution. Both ways are good, but if you think of User as of the main entity while Profile is a property then you should put GroupId into User, otherwise, if you mean User and Profile as a single entity, you can leave GroupId in Profile, and by this you're not saying group HAS MANY profiles but group HAS MANY users.
By setting a proper one-to-one relation (User-Profile) you can force your data integrity good enough.

Handling roles by dividing users in multiple tables/models

My application has several kinds of users with different sets of properties, for example: customers and employees. One user may be belong to more than one of these roles.
So I decided to have this (simplified example) structure of my database:
users (contains the fields applicable to all different kinds of users)
id
username
email
customers (contains fields applicable to customer role)
id
user_id
shipping_address
employees (contains fields applicable to employee role)
id
user_id
salary
Question 1: Is this a good way of handling user roles with many different properties?
In reality I have six roles with sets of 7-20 properties. Some of the roles are not even conventionar roles, but bore like extra properties. If a user decides to login with a Facebook acoount, then the Facebook_account table is applicable.
Question 2: When using an ORM, in my case Eloquent, would it make sense to have the role model (customers) extend the user model?
When I fetch a customer object I really want all user och customer properties together, without having to merge them manually.