has_many :through model names, controller and attributes best practices?
naming-conventions, rails-activerecord, ruby-on-rails
Solution
I would use a specific controller. Even if now the interaction sounds simple, you can't know if in the future you'll need to add more advanced features. I've been handling these kind of relationships in several projects, and using a controller for the join model has always paid off.
You can structure it this way, for example:
- `index` should expect a `params[:project_id]`, so that you can display only the index of users for a specific project.
- `create` is where you add new users, that is where you create new join models.
- `update` is to modify a value on an existing join model, for example when you want to update the role of a user in a project.
- `destroy` is where you remove users from the project, that is where you delete the corresponding join models.
- You might not need a `show` and `edit` actions, if you decide to manage everything in the `index` view.
Also, I'd suggest to choose a different name. Rails relies heavily on naming conventions, and `projects_users` is the default name for the join_table you would use with a `has_and_belongs_to_many` association. In theory you can use it for an independent model (and a `has_many through:`), but it's not immediately clear and you might break something. In addiction, it will confuse the hell out of any new programmer that could join the project in the future (personal experience).
What about calling the model something like `project_participation`?
If you haven't built a lot of functionality yet, and don't have yet that table in production, changing it now will save you a lot of headaches in the future.
update
1) I stand by what I said earlier: your join model is a full fledged record, it holds state, can be fetched, modified (by the user) and destroyed. A dedicated controller is the way to go. Also, this controller should handle all the operations that modify the join model, that is that alter its properties.
2) You can define `User#role_for(project)`, just remember that it should properly handle the situation where the `user` is not participating to the `project`.
You can also make it explicit with something like:
@user.project_participations.where(project_id: @project.id).first.try(:role)
# or...
ProjectParticipation.find_by(project_id: @project.id, user_id: @user.id).try(:role)
But I'd say that encapsulating this logic in a method (on one of the two models) would be better.
3) You are already using a non standard name for your table. What I mean is that it's the default name for a different kind of association (`has_and_belongs_to_many`), not the one you are using (`has_many through:`). Ask yourself this: is the table backing an actual model? If yes, that model represents something in the real world, and thus should have an appropriate name. If, on the other hand, the table is not backing a model (e.g. it's a join table), then you should combine the names of the tables (models) it's joining.
Problem
Disclaimer: I really spent time thinking about names of models and variables. If you also do, this question is for you. I have a Rails project which contains two models: `User` and `Project`. They are connected by the model `ProjectsUser`, which is a connection model in a many-to-many relationship. This model also holds the `role` of a user in the given project, along with other attributes such as `tier` and `departments`. So this is a `has_many :through` relationship. Given this scenario, here is everything that always bothered me on all my rails projects since I started developing on it: Should I use a `ProjectsUserController` or better add the relevant actions on `UserController` and `ProjectController`? At some point, I want to assign users to a project, or even changing the role of a user in a given project. Is it a better practice to leave those actions on the connection controller, or use the model controllers? Should I write a method to get the role of a user for a given project? This is basically if I should have a method `User#role_for(project)` or not. Since this method basically is getting the information from the `projects_user` object it could make more sense to always let this explicity on the code, since most of the times I'll have the `project` and the `user`, but not the `projects_user`. Is this line of thinking correct, or maybe the problem is that I'm should have more `project_user` on my code than I really do? Are there good caveats for this? Should I try to rename my table to a non-standard name if it is not obvious? Ok, I got that if I have the models `User` and `NewsSite` I should use `has_many :subscriptions`, but the thing is that naming those models in real life cases are usually harder, by my experience. When the name ends up not being that obvious (for exemple, in my case, maybe `project_participation` as @wonderingtomato suggested) is for the best, or in those cases it is better to fall back to the `ProjectsUser` approach? One extra cookie for pointing beautiful open source Rails code, or by book indications that might help with my kind of questions.