How to access the joining model on a has_many :through relationship
I have a typical many-to-many relationship using has_many => :through
, as detailed below.
class member
has_many member_roles
has_many roles, :through => :member_roles
end
class role
has_many member_roles
has_man members, :through => :member_roles
end
class member_role
belongs_to :member
belongs_to :role
# has following fields: member_id, role_id, scope, sport_id
end
What I'm trying to do here is allow members to be assigned roles. Each member role has a scope, which by default is set to "All" but if desired can be set to "Sport". If the scope is set to sport then we also capture the sport_id, which allows us to restrict assess on that role to a particular sport (ie, can only manage the teams of that sport, rather than the teams of every sport). Sounds simple enough.
I have setup my update_member_roles
action something like this:
def update
# Assume we passing a param like: params[:member][:roles]
# as an array of hashes consisting of :role_id and if开发者_JS百科 set, :sport_id
roles = (params[:member] ||= {}).delete "roles"
@member.roles = Role.find_all_by_id(roles.map{|r| r["role_id"]})
if @member.update_attributes params[:member]
flash[:notice] = "Roles successfully updated."
redirect_to member_path(@member)
else
render :action => "edit"
end
end
The above works nice enough, it sets the appropriate member_roles very nicely... but as I'm working on the Role model and not the MemberRole model I'm kind of stuck as to how I can access the joining model to set the :scope and :sport_id.
Any pointers here would be much appreciated.
You should use the member_roles
association instead of roles
association.
def update
# The role hash should contain :role_id, :scope and if set, :sport_id
roles = ((params[:member] ||= {}).delete "roles") || []
MemberRole.transaction do
# Next line will associate the :sport_id and :scope to member_roles
# EDIT: Changed the code to flush old roles.
@member.member_roles = roles.collect{|r| MemberRole.new(r)}
# Next line will save the member attributes and member_roles in one TX
if @member.update_attributes params[:member]
# some code
else
# some code
end
end
end
Make sure you enclose everything in one transaction.
Other possibility is to use accepts_nested_attributes_for
on member_roles
association.
Sounds like you should look into using nested attributes:
- Railscast #196
- Railscast #197
- Rails API docs
精彩评论