oLr*_*raX 5 ruby ruby-on-rails has-many-through nested-attributes strong-parameters
这是我的3个型号.
User
has_many :memberships
has_many :teams, through: :memberships, dependent: :destroy
accepts_nested_attributes_for :memberships
Team
has_many :memberships
has_many :users, through: :memberships, dependent: :destroy
accepts_nested_attributes_for :memberships
Membership
belongs_to :team
belongs_to :user
Run Code Online (Sandbox Code Playgroud)
以下是我的Team控制器的一些部分.我的目标是向某个团队添加/更新成员.请注意,添加成员的源已作为一组用户存在.
TeamsController
def create
@team = Team.new(team_params)
@team.users << User.find(member_ids) #add leader and members to team
if @team.save
#redirect to created team
else
#show errors
end
end
def update
#TO DO: update team roster here
if @team.update(team_params)
#redirect to updated team
else
#show errors
end
end
Run Code Online (Sandbox Code Playgroud)
Team控制器的强参数
#parameters for team details
def team_params
params.require(:team).permit(:name, :department)
end
#parameters for members (includes leader)
def members_params
params.require(:team).permit(:leader, members:[])
end
#get id values from members_params and store in an array
def member_ids
members_params.values.flatten
end
Run Code Online (Sandbox Code Playgroud)
对于表单,我只有:
我可以成功创建一个团队,同时在我的创建上传递验证(包括团队和成员模型).但是,我不知道如何更新团队,因为如果我使用@team.users.clear然后简单地从create做同样的事情(我知道,这样做有点愚蠢),它会验证,但它会保存它,无论如何是否有错误.
表格代码
<%= form_for(@team, remote: true) do |f| %>
<%= f.label :name, "Name" %>
<%= f.text_field :name %>
<%= f.label :department, "Department" %>
<%= f.select :department, options_for_select(["Architectural", "Interior Design"], department), include_blank: true %>
<%= f.label :leader, "Leader" %>
<%= f.select :leader, select_leaders(department, @team.id), {include_blank: true, selected: pre_select(:leader)} %>
<%= f.label :members, "Members" %>
<%= f.select :members, select_members(department, @team.id), {include_blank: true}, {id: "team_members", multiple: :multiple, data: {member_ids: pre_select(:members)}}%>
<% end %>
Run Code Online (Sandbox Code Playgroud)
表格注意事项:
:members字段已启用select2.所以我的问题是:
一些选择导致解决方案
选项#1(迄今为止的最佳解决方案)
我只为此做了一个急救解决方案,所以我认为有一个比我下面做的更好的方法.我在这里做的是创建用户参数,其中使用从member_ids中找到的用户作为值.
TeamsController
def create
team = Team.new(team_params.merge({users: User.find(member_ids)}))
...
end
def update
...
if @team.update(team_params.merge({users: User.find(member_ids)}))
..
end
Run Code Online (Sandbox Code Playgroud)
选项#2
独立于解决方案1,我只有team_params强大的参数.
TeamsController
...
private
def team_params
params.require(:team).permit(:name, :department, :leader, members:[])
end
Run Code Online (Sandbox Code Playgroud)
我为领导者和成员创建了setter方法.但似乎成员覆盖了领导者setter,因为我将这个update方法用于两个setter,并且更新使用与用户相同的资源.使用此选项似乎可以解决方法.
Team
...
def leader=(leader_id)
#self.update(users: User.find(leader_id))
end
def members=(members_ids)
#self.update(users: User.find(members_id))
end
Run Code Online (Sandbox Code Playgroud)
因为,领导者和成员在你的场景中并没有那么不同.您可以将模型和视图表单更改为以下内容:
class Team < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships, dependent: :destroy
accepts_nested_attributes_for :memberships
end
class User < ActiveRecord::Base
has_many :memberships
has_many :teams, through: :memberships, dependent: :destroy
end
class Membership < ActiveRecord::Base
belongs_to :team
belongs_to :user
accepts_nested_attributes_for :user
end
Run Code Online (Sandbox Code Playgroud)
并形成视图代码:
<%= form_for(@team) do |f| %>
<% if @team.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@team.errors.count, "error") %> prohibited this team from being saved:</h2>
<ul>
<% @team.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<%= f.fields_for :memberships do |m| %>
<div class="field">
<%= m.label :memberships_name %><br>
<%= m.text_field :name %>
</div>
<%= m.fields_for :user do |u| %>
<div class="field">
<%= u.label :user_name %><br>
<%= u.text_field :name %>
</div>
<% end %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Run Code Online (Sandbox Code Playgroud)
此外,请确保您在控制器中更改此设置:
# GET /teams/new
def new
@team = Team.new
3.times do # number of members you need to generate!
@team.memberships.build{ |m| m.build_user }
end
end
# GET /teams/1/edit
def edit
end
# POST /teams
# POST /teams.json
def create
@team = Team.new(team_params)
respond_to do |format|
if @team.save
format.html { redirect_to @team, notice: 'Team was successfully created.' }
format.json { render action: 'show', status: :created, location: @team }
else
format.html { render action: 'new' }
format.json { render json: @team.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_team
@team = Team.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def team_params
params.require(:team).permit(:name, memberships_attributes: [:id, :name, user_attributes: [:id, :name]])
end
Run Code Online (Sandbox Code Playgroud)
虽然您可以在Rails控制台中执行此操作以执行快速代码验证:
team_params = {"name"=>"Team", "memberships_attributes"=>{"0"=>{"name"=>"Membership 1", "user_attributes"=>{"name"=>"User 1"}}, "1"=>{"name"=>"Membership 2", "user_attributes"=>{"name"=>"User 2"}}, "2"=>{"name"=>"Membership 3", "user_attributes"=>{"name"=>"User 3"}}}}
team = Team.new(team_params)
team.save
team.users
#=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 1, name: "User 1", email: nil, created_at: "2014-09-04 11:25:48", updated_at: "2014-09-04 11:25:48">, #<User id: 2, name: "User 2", email: nil, created_at: "2014-09-04 11:25:48", updated_at: "2014-09-04 11:25:48">, #<User id: 3, name: "User 3", email: nil, created_at: "2014-09-04 11:25:48", updated_at: "2014-09-04 11:25:48">]>
Run Code Online (Sandbox Code Playgroud)
我希望它有所帮助.