Modelling parent/child accounts with other models

I am creating an application that involves:

Accounts People (Many people belong to one account)

..and various other models...

Companies Projects Services ...

I'm trying to find what the best way is to denote that any of the non-account related model records belong to an account. So using the tables listed above, what is the best way to show that any projects, services, and companies created by the active account belong to that account?

Should I use my authentication system to grab the account ID related to the person signed in? Should I add a foreign key to all the other models to denote that any records created belong to the active account?

It seems like most of my look-ups will be based on records relevant to the current account. I'd greatly appreciate any tips.

Thanks, David

If the models are always related to a single account then have Company belongs_to account, Account has_many companies and so on. Then if you have an account @account, the companies are @account.companies.

Colin

This has always bothered me and the best answer I've come up with is "It depends"! There are at least three, but probably more, scenarios.

1. A "Basecamp" style application where you log in to an account (that has people/user, invoices, companies, venders etc). Each account appears to have it own database. How do you keep Foo's account from seeing Bar's invoices? You must have a belongs to relation on (at least) each major path. If you have a deeply nested structure, but use the sane approach of shallow routing you have to deal with things like "/invoices/2342" in the url and make sure that invoice ID 2342 belongs to a customer that belongs to your account. That leads me to believe that you have to have a before filter on almost everything that checks that resource belongs to that account. That leads to the question on what is the best way to handle that (which is one way your question could be interpreted).

  Do make it easy on myself and put account_id in EVERY resource? (before filter has what it needs - I have account_id in current_user)   Do I put account_id in in the root of every major nested path and get the root resource for any query on a child resource? (before filter can get what it needs)

Getting the related resources is fairly easy (current_user.account.companies or @account.companies), but that does not stop someone from modifying the id in the url.

2. An "Accountant" type application where the user can access all the accounts. This is probably not much different than the "Basecamp" approach, it is just a super user that probably does not want Foo's invoices mixed with Bar's invoices (but may want the option - which is 3) and start with account. Url modification is probably not a concern since they have access to everything anyhow.

3. The account relations are there, but everything is mixed together and you need filters or scopes to separate them when they need to be separated.

This probably didn't answer your question (or mine), but the before filter is probably the key - it controls what rules you are trying to apply. Now we just have to get an answer to the second part of your question. What is the most efficient way?

I still don't know what the best approach is for the "Basecamp" type scenario.

  @resources = current_user.account.resources # for index or get many   @resource = current_user.account.resources.find(params[:id]) # for get one   or   @resource = Resource.find(params[:id])   if !@resource.account_id?(current_user.account_id) # some method in model that compares resource account_id = users account_id     redirect_to "Unauthorized access"   end

Steve

This has always bothered me and the best answer I've come up with is "It depends"! There are at least three, but probably more, scenarios.

1. A "Basecamp" style application where you log in to an account (that has people/user, invoices, companies, venders etc). Each account appears to have it own database. How do you keep Foo's account from seeing Bar's invoices? You must have a belongs to relation on (at least) each major path. If you have a deeply nested structure, but use the sane approach of shallow routing you have to deal with things like "/invoices/2342" in the url and make sure that invoice ID 2342 belongs to a customer that belongs to your account. That leads me to believe that you have to have a before filter on almost everything that checks that resource belongs to that account. That leads to the question on what is the best way to handle that (which is one way your question could be interpreted).

  Do make it easy on myself and put account_id in EVERY resource? (before filter has what it needs - I have account_id in current_user)   Do I put account_id in in the root of every major nested path and get the root resource for any query on a child resource? (before filter can get what it needs)

Getting the related resources is fairly easy (current_user.account.companies or @account.companies), but that does not stop someone from modifying the id in the url.

2. An "Accountant" type application where the user can access all the accounts. This is probably not much different than the "Basecamp" approach, it is just a super user that probably does not want Foo's invoices mixed with Bar's invoices (but may want the option - which is 3) and start with account. Url modification is probably not a concern since they have access to everything anyhow.

3. The account relations are there, but everything is mixed together and you need filters or scopes to separate them when they need to be separated.

This probably didn't answer your question (or mine), but the before filter is probably the key - it controls what rules you are trying to apply. Now we just have to get an answer to the second part of your question. What is the most efficient way?

I still don't know what the best approach is for the "Basecamp" type scenario.

  @resources = current_user.account.resources # for index or get many   @resource = current_user.account.resources.find(params[:id]) # for get one   or   @resource = Resource.find(params[:id])   if !@resource.account_id?(current_user.account_id) # some method in model that compares resource account_id = users account_id     redirect_to "Unauthorized access"   end

Steve

Sorry for double post, it said my session had timed out and didn't think the first one went through. Don't see a way to delete the post.

This is a mailing list so once sent you cannot delete it. Otherwise you would need access to my PC (for example) to delete it from my inbox.

Colin

Thanks for the responses. I think my application probably matches the Basecamp style of account handling. I think for simplicity I can keep everything project-centric and use the accounts to show who owns which projects

@account.projects @account.projects.companies @account.projects.people @account.projects.services

people belongs_to companies companies has_many people

I like the idea of leaving a foreign key on all resources to show who the account belongs to. It's nice to know I can use a before filter to handle it.

Playing around with my new model I feel like I've made a good decision with the modeling.

If I were to make an ER diagram, I've effectively made a sandwich with Projects on the top, all my supporting models and relationships in the middle, and People and Accounts on the bottom. I've linked Accounts to Projects as well.

I'll post more on my success or problems with this approach.