views:

286

answers:

2

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.

+5  A: 

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.

Raimonds Simanovskis
+1  A: 

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.

tvanfosson
Interesting: I'd much prefer the constant 3 to be captured in a readily accessible location (the DB constraint) than scattered all over the code.
Jonathan Leffler
This constant 3 wouldn't be scattered all over code - it would be just in this model class. And "Rails way" is to put all business rules in one place in your models and not to use database specific triggers or stored procedures.
Raimonds Simanovskis
The constant, n, would actually be something that could be different per-account, and that information would be stored with the account itself.
Eliza
This would be easy to implement:... if user.account.users.size >= user.account.max_number_of_users(where max_number_of_users would be attribute of Account model).
Raimonds Simanovskis