Accessing current_user from within a model: just don't do it
I came across a situation where I thought I wanted to access the
current_user from within a model, but it was just a case of mistaken thinking. Making session variables like
current_user accessible to a model is a break with convention, and breaking with conventions is usually a bad idea. So, the rule is: don’t do it unless you have exhausted all other options. There is a probably an easier way of doing what you are trying to do.
Here was my situation. I had users logging in to an application and signing up for various groups. The relationship between models was as follows:
class Users < ActiveRecord::Base has_many :memberships, :dependent => :destroy has_many :groups, :through => :memberships end class Groups < ActiveRecord::Base has_many :memberships, :dependent => :destroy has_many :users, :through => :memberships end class Memeberships < ActiveRecord::Base belongs_to :user belongs_to :group end
When a user created a new group, I wanted them to be added automatically to the group through a membership table, so at first I did something like this:
@group = Group.create(params[:group])
But, in doing this, I had already made an elementary mistake. Assuming the
current_user is accessible to the controller, something like this would obviously have been better:
@group = current_user.groups.create(params[:group])
In other words, if I had written
current_user.groups.create instead of just
Group.create, the Membership table would have been updated automatically.
As an alternative, say I wanted to set up the
current_user as the owner of the group, I could possibly do this:
@group = current_user.own_groups.create(params[:group])
Instead of adding the
current_user as a member of the new group, I could make the
current_user the owner (i.e. it’s the current_user’s ‘own group’). This would also need the following new line in the User model:
has_many :own_groups, :through => :memberships, :class_name => 'Group', :foreign_key => 'group_id'
And in the Group model:
has_one :owner, :through => :memberships, :class_name => 'User', :foreign_key => "id"
However, there is another—and my preferred—way of doing this. Rather than have the relationship defined in the Group model as a foreign key, the ‘ownership’ of the group can be defined in the Membership model, as a kind of membership.
This keeps the responsibility where it probably belongs, in the relationship (i.e. Membership) model. It also makes it easier to change the definition of ‘ownership’ at a future date, so that a group could have more than one ‘owner’. In other words, it’s a more flexible design.