validates_uniqueness_of scope on other table - ruby-on-rails

A User belongs to Groups, a Group belongs to a Company
I want to verify that the username of the user is unique per company. So my database can have multiple dublicate usernames as long as they belong to a different company.
A logical step would be to use validates_uniqueness _of scope and use the company id (stored in groups). Is there a way scope can get access to other tables or do I need to solve this ina different way?
thanks

In this design, I see a problem, if a User can belong to one group, and that a group can belong to one company. But the association of User and Company is very indirect, where as in reality it should be direct, independent of its association with the group.
So for example to change companies a User switches groups, not companies, which is not entirely right. It may make sense in your context I am not sure.
So storing company information in the User table would make sense ( as for uniqueness test as well, the relationship is direct).
and
validation_uniqueness_of :user, :scope=> company_id
would work.
Update
I am not asking you to denormalize tables, the group still may be tightly tied to the company. But that relationship has nothing to do with the user-company
My point is basically, User object should have direct visibility into the Company class, not through "Group".
That means adding a company column into your table, thats all, and establishing a direct relationship b/w user and Company.
and it wouldn't be so much of a management hassle.Plus, I sort of expect, to write something like
"user.company"
Instead of
"user.group.company".
In any case. Thats just my view, I don't know the whole context, so my advice is based on what I saw in your question

Making a custom validation rules is probably the best way to tackle this problem.

I had this issue as well and came to research an answer. I used Tarscher answer to come up with the following solution.
validates_each :username do |record, attr, value|
if User.joins(:group).where('username = ? and groups.company_id = ?', record.username, record.group.company_id).present?
record.errors.add attr, 'This username is already taken by some on in your company'
end
end

Related

Rails: using STI to model client and partner records

I know STI is a debated topic within the Rails community (and probably others), which is the reason I'm trying to find a different solution to my problem before going down the STI route.
I'm building a system that has a contact management portion, which contains both client and partner records. The difference is that partners will have an associated partner_type and a few additional fields that client will not have.
This looks like a good case for STI. The records are of the same "category", meaning they all represent "people" but in different ways. They will all have the same core fields and have many email_addresses/phone_numbers.
But the biggest requirement that led me to STI instead of separate tables, is that I need to list all of the contacts together alphabetically. My employer doesn't want separate pages for client records and partner records. If I broke this into multiple tables, I would have to somehow query both tables and arrange them alphabetically, also while taking pagination into account (will have thousands of records for each type).
Is there another solution besides STI? I know many developers have run into problems with STI before, but I'm leaning towards this is a text-book case where STI may actually work.
class Contact < ApplicationRecord
has_many :email_addresses # probably use polymorphic
has_many :phone_numbers # probably use polymorphic
validates :first_name, :last_name, presence: true
end
class Client < Contact
end
class Partner < Contact
belongs_to :partner_type
validates :partner_type, presence: true
# some attributes only applicable to client
validates :client_unique_field1, :client_unique_field2, presence: true
end
There are two design decisions you need to make this case:
Should partners and clients share the same table?
a. If "no", then you simple create separate tables and separate models.
b. If "yes", then you have a second design question to answer #2.
Should partners and clients share the same model class?
a. If "yes", then you can use an emum to identify the different roles of partner and client and use that enum to drive your business logic.
b. If "no" then you should implement STI.
I think there is a strong case to say "yes" to #1. It seems client and partner are both fundamentally the same thing. They are both people.
More importantly, they will contain most of the same information so sharing a table makes good sense.
So that leaves you with whether or not to use STI or an enum. The fundamental decision you need to make surrounds business logic associated with partners and clients.
If most of the business logic is shared, then it makes sense to use an enum. Let me give you an example. In one of my projects, I have a User model. All users can do basic things on the site. However, we also have school_admin users and class_admin users. Admins of course have greater access to portions of the site, but from a business logic perspective, there are only a couple of relations and a couple of methods that are unique to an admin and not shared by a user.
Since 95% of the business logic is shared between normal users and admins, I elected to keep them all in one class. I used an enum called role to distinguish users:
# in the User model
enum :role, [:graduate, :school_admin, :class_admin]
In the users table I have a column of type int called role. The enum opens up a bunch of helper methods, such as class_admin?, to make the business logic work.
Your case may be different. It seems clients and partners may have greater differences in business logic in your app. I don't know, but it sounds like there are some fundamental differences in their roles. You will have to decide how much business logic is shared between them and how much is different. If they are different enough, then STI makes sense.
Furthermore, you may want to go the STI route if you would like to take advantage of inheritance in methods. For example: you may have a contact_verified? method where partner.contact_verified? has different business logic (email and phone maybe) than client.contact_verified? (email only). A weak example maybe, but you get the idea. Of course, you could accomplish the same thing with a conditional inside contact_verified? when using the single model approach.
You are correct that the some in the Rails community tend to be down on STI. So do not make the decision to go the STI route lightly. However, I have used STI successfully in some apps with few STI-related problems.
It all depends on how much business logic is shared and if you want to take advantage of inheritance. The decision is ultimately up to you.
Tom Aranda gives a good framework for deciding on an approach (and it seems you should probably use one table). Your "biggest" requirement, however, could easily be solved in SQL with a UNION query even if you decided to use two tables.
SELECT * FROM (SELECT id, 'Client' as type, first_name, last_name FROM clients
UNION SELECT id, 'Partner' as type, first_name, last_name FROM partners) AS t1
ORDER BY last_name LIMIT 25;
You could go further and INNER JOIN the email addresses and phone numbers as well.

set attributes in a has_many_though relationship in Rails

I have three models: Company, User and Employment. Each Company has many users though their Employments, and a user might belong to several companies through his employments.
Now, the tricky part: let's say my User1 belongs to 2 companies. He is SUPER_ADMIN in the first compnay, but just BASIC_USER in the second one. What would be the best way to define his roles ?
I used to have a simple has_many relationship between Company and User, which allowed me to simply set a is_admin attr on my User, but obviously this won't do with the new HMT relationship.
I thought of defining an array of IDs for each company, that would include the IDs of each admin user, but I'm pretty sure there is a cleaner way around.
In above scenario, you can use intermediate table i.e. Employment table to save all details of a user and it's associate company.
As Employment table will have ids of both user & company it will be easy for you to keep extra information related to user and company. Just add role column in this and use this to get information

Two simple questions about STI implementation

I am using STI for my user models. I have an User class, and the subclasses Seller and Customer. A user cannot be both, so I think STI is alright for this case.
I have two questions:
How do I restrict the creation of User instances, so only sellers or customers can be created? I guess I could validate the presence of Type, but that doesnt feel very right to me.
Can I have extra, different fields for sellers and customers with STI? How?
You can only allow saving of users and customers by doing:
validates_inclusion_of :type, :in => [:customer, :user]
As far as your second question, the answer is: add columns. If you add columns for the customer the user model will ignore them, so no big deal. It depends on your use case though, in some cases it's best to avoid STI.

Rails model structures

I saw some threads on this already on Stack, but wanted a little more clarification.
I have seen many apps where there is a product model and category category model. This is a has and belongs to many association, or a has_many through association.
I have also seen many apps where there is a user model and an email_address model. Email_address belongs to user, but user can have many email addresses.
My question is, would there ever be a situation where you can lump all the email addresses or categories into the user and product models, respectively? So in your user model, you will have email_one, email_two, etc?
What are the pros and cons of breaking it into different models? Thanks.
If the attribute is simple, it's almost certainly best to keep it in a single model - you can even serialize the attribute so that it takes, for example, and array of email_addresses. BUT (big but) you may well want to add a lot more information to an email address - which one is the primary one, when was it last profiled, email last sent to .. etc etc. This of course is much easier to handle if you have a separate email address model. So perhaps the question is really 'when should i use serialized attributes?'. My own answer would be 'only if I'm sure that I am storing something in that field that I never want to add further attributes to'. Usually that means it is something pretty peripheral to the main application, and about which no-one cares very much ...

Best practice for limiting the number of associations within a has_many relationship?

Say that I have two models- Users and Accounts. Each account can have at most n users associated with it, and a user can only be associated with one account.
It would seem natural to say that User
belongs_to :account
and Account
has_many :users
However, I'm not clear on the best practice when it comes to limiting the number of associations through that has_many declaration. I know that there is a :limit argument, but that that only limits the number of associations returned, not the number that are able to exist.
I suspect that the answer is to use something like :before_add. However, that approach seems only to apply to associations created via << . So it would get called when you used
#account.users << someuser
but not if you used
#account.users.create
I had also considered that it might be more practical to implement the limit using before_save within the User model, but it seems like it would be a bit off to implement Account business rules within the User model.
What is the best practice for limiting the number of associations?
Edit: the n users per account would be some business data that is stored within the individual accounts, rather than being a straight up magic number that would be floating around willy nilly in the code.
At first, if your users table has foreign key account_id then you need to use
class User
belongs_to :account
end
In this way you will ensure that User can be associated just to one Account.
If you want to limit that Account can have e.g. at most 3 users then you can define the following validation:
class User
validates_each :account do |user, attr, value|
user.errors.add attr, "too much users for account" if user.account.users.size >= 3
end
end
and as a result you will not be able to create new user for account if account has already 3 users.
I think your rule is subject to a different interpretation. Think of the rule as being "You cannot add a user to an account that already has 3 users." Now that it is a user rule, implementing it on the user object seems perfectly natural and #Raimond's solution will suffice.
You could also think about implementing this as a database constraint, but I probably wouldn't go that way...3 seems to be an arbitrary number that may change later and I, and I suspect you, would prefer that it be captured in the code rather than hidden away in a DB constraint.

Resources