使用 rails 中的嵌套 form_for 更新多对多
Updating many to many using a Nested form_for in rails
这是一个非常普遍的问题,有数十篇博客 post 介绍了这个问题。我只是不知道我做错了什么。 很抱歉 post.
我有一个Mono-Transitive many-to-many relationship。
一个会话可以有多个字幕员。字幕员可能有多个会话。多对多关系是由"Membership"完成的。
class Session < ApplicationRecord
# This will tell to delete all the memberships
# in case the current session is deleted.
has_many :memberships, dependent: :destroy
# Added this, see:
# https://www.sitepoint.com/master-many-to-many-associations-with-activerecord/
has_many :captionists, through: :memberships
# https://www.sitepoint.com/complex-rails-forms-with-nested-attributes/
# Allows us to delete the membersihps if not included.
accepts_nested_attributes_for :memberships, :allow_destroy => true
validates :uuid, presence: true, uniqueness: true
validates :name, presence: true, uniqueness: false
validates :url, presence: true, uniqueness: true
validates :passcode, presence: true, uniqueness: true
validates :shortcode, presence: true, uniqueness: true
end
class Captionist < ApplicationRecord
has_many :memberships
# Added this, see:
# https://www.sitepoint.com/master-many-to-many-associations-with-activerecord/
has_many :sessions, through: :memberships
validates :uuid, presence: true, uniqueness: true
validates :full_name, presence: true, uniqueness: false
validates :access_token, presence: true, uniqueness: true
validates :passcode, presence: true, uniqueness: false
end
# Mono Transitive Association
# https://www.sitepoint.com/master-many-to-many-associations-with-activerecord/
class Membership < ApplicationRecord
belongs_to :session
belongs_to :captionist
validates :uuid, presence: true, uniqueness: true
validates :hostname, presence: true, uniqueness: false
validates :web_port, presence: true, uniqueness: false
validates :tcp_port, presence: true, uniqueness: false
end
这是我的 session_controller(在管理命名空间下)
module Admin
class SessionsController < Admin::BaseAdminController
def index
# List of sessions
# Careful on not to iterate through everything, as Rails will interpret it
# as all.
# Default is #25
# This uses the kaminari gem.
# https://github.com/kaminari/kaminari
@sessions = Session.page(params[:page] || 1)
@page_count = @sessions.total_pages
end
def edit
@session = Session.includes(:memberships).includes(:captionists).find(params[:id])
if !@session
raise ActionController::RoutingError.new('Not Found')
end
end
def update
@session = Session.find(params[:id])
if @session.update_attributes(session_params)
respond_to do |format|
format.html { redirect_to edit_admin_session_path , notice: 'Session was successfully updated.' }
end
return
end
render 'edit'
end
def session_params
return params.require(:session).permit(:name, :passcode, :shortcode,
membership_attributes: [:id, :hostname, :tag_teaming, :web_port, :tcp_port, :allowing_connections, :_destroy])
end
end
end
最后但同样重要的是 .erb 文件:
<h1>Editting!!</h1>
<%= link_to 'Go Back To list', admin_sessions_path %>
<%= form_for :session, method: :patch, class:'container' do |f| %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %><br />
</div>
<div class="form-group">
<%= f.label :passcode %>
<%= f.text_field :passcode, class: 'form-control' %><br />
</div>
<%= f.label :shortcode %>
<%= f.text_field :shortcode, class: 'form-control' %><br />
<%= f.label :memberships %>
<table class="table table-striped table-hover">
<thead class="thead-dark">
<tr>
<th scope="col"> Allowing Connections </th>
<th scope="col">Tag Teaming</th>
<th scope="col">Hostname</th>
<th scope="col">Web Port</th>
<th scope="col">TCP Port</th>
<th scope="col">Captionist Name </th>
<th scope="col">Action</th>
</tr>
</thead>
<!-- HERE'S THE PART THAT is giving me the problem -->
<% @session.memberships.map do |membership| %>
<%= f.fields_for membership do |ff| %>
<tr class="nested-fields">
<%= ff.hidden_field :id %>
<td>
<%= ff.label :allowing_connections %>
<%= ff.check_box :allowing_connections %>
</td>
<td>
<%= ff.label :tag_teaming %>
<%= ff.check_box :tag_teaming %>
</td>
<td>
<%= ff.label :hostname %>
<%= ff.text_field :hostname %>
</td>
<td>
<%= ff.label :web_port %>
<%= ff.text_field :web_port %>
</td>
<td>
<%= ff.label :tcp_port %>
<%= ff.text_field :tcp_port %>
</td>
<td>
<%= membership.captionist.full_name %>
</td>
<td>
<%= ff.check_box :_destroy %>
</td>
</tr>
<% end %>
<% end %>
</table>
<%= f.submit 'Update', class: 'btn btn-primary' %>
<% end %>
发送的参数如下:
{"name"=>"Session Correct name", "passcode"=>"A Passcode", "shortcode"=>"A Shortcode", "membership"=>{"id"=>"106", "allowing_connections"=>"0", "tag_teaming"=>"0", "hostname"=>"delete hostname", "web_port"=>"80", "tcp_port"=>"3001", "_destroy"=>"0"}} permitted: false>
以下是我将参数列入白名单的方式:
return params.require(:session).permit(:name, :passcode, :shortcode,
membership_attributes: [:id, :hostname, :tag_teaming, :web_port, :tcp_port, :allowing_connections, :_destroy])
有什么问题?
1) 现在,如果我提交表单,我会在控制台末尾得到:unpermitted_params=["membership"]
2) 我检查过数据库是否已正确迁移。外键设置正确。我可以毫无问题地浏览属性。我所做的是在执行迁移后添加关系 has_many。我不知道我是否需要添加某种迁移才能使更新正常工作。
3) 我试过替换
<%= form_for :session, method: :patch, class:'container' do |f| %>
对于:
<%= form_for @session, 方法: :patch, class:'container' do |f| %>
但是我收到一个错误:
Could not find a valid mapping for #
4) 我也尝试删除 <% @session.memberships.map do |membership| %>
,但我不会填充这些字段。我看到的另一个问题是输入的名称都相同。每当我发送要更新的参数时,它只会反映嵌套部分的最后一部分。
这是服务器的响应(params[:session]):
Parameters {"name"=>"Session Correct name", "passcode"=>"A Passcode", "shortcode"=>"A Shortcode", "membership"=>{"id"=>"106", "allowing_connections"=>"0", "tag_teaming"=>"0", "hostname"=>"delete hostname", "web_port"=>"80", "tcp_port"=>"3001", "_destroy"=>"0"}} permitted: false>
这是一个视觉效果示例:
未嵌套的部分得到更新。
有什么想法吗?
谢谢!
编辑(2018 年 4 月 9 日):添加了发送的参数(我这里没有)。修复了格式和 HTML.
的问题
我很确定这只是一个复数问题。我正在研究与您的代码类似的代码,看起来您想要 memberships_attributes
在您的强参数中。
还要检查正在生成的 HTML,并确保您允许的密钥匹配。
我建议在控制器中放置一个 binding.pry 或任何你喜欢调试的东西,然后尝试用传入的参数保存对象。
你不需要:
<% @session.memberships.map do |membership| %>
<%= f.fields_for membership do |ff| %>
您只需要:
<%= f.fields_for @session.memberships do |ff| %>
Rails 将遍历会话中的所有成员并将所有字段放入 memberships_attributes.
这是一个非常普遍的问题,有数十篇博客 post 介绍了这个问题。我只是不知道我做错了什么。 很抱歉 post.
我有一个Mono-Transitive many-to-many relationship。
一个会话可以有多个字幕员。字幕员可能有多个会话。多对多关系是由"Membership"完成的。
class Session < ApplicationRecord
# This will tell to delete all the memberships
# in case the current session is deleted.
has_many :memberships, dependent: :destroy
# Added this, see:
# https://www.sitepoint.com/master-many-to-many-associations-with-activerecord/
has_many :captionists, through: :memberships
# https://www.sitepoint.com/complex-rails-forms-with-nested-attributes/
# Allows us to delete the membersihps if not included.
accepts_nested_attributes_for :memberships, :allow_destroy => true
validates :uuid, presence: true, uniqueness: true
validates :name, presence: true, uniqueness: false
validates :url, presence: true, uniqueness: true
validates :passcode, presence: true, uniqueness: true
validates :shortcode, presence: true, uniqueness: true
end
class Captionist < ApplicationRecord
has_many :memberships
# Added this, see:
# https://www.sitepoint.com/master-many-to-many-associations-with-activerecord/
has_many :sessions, through: :memberships
validates :uuid, presence: true, uniqueness: true
validates :full_name, presence: true, uniqueness: false
validates :access_token, presence: true, uniqueness: true
validates :passcode, presence: true, uniqueness: false
end
# Mono Transitive Association
# https://www.sitepoint.com/master-many-to-many-associations-with-activerecord/
class Membership < ApplicationRecord
belongs_to :session
belongs_to :captionist
validates :uuid, presence: true, uniqueness: true
validates :hostname, presence: true, uniqueness: false
validates :web_port, presence: true, uniqueness: false
validates :tcp_port, presence: true, uniqueness: false
end
这是我的 session_controller(在管理命名空间下)
module Admin
class SessionsController < Admin::BaseAdminController
def index
# List of sessions
# Careful on not to iterate through everything, as Rails will interpret it
# as all.
# Default is #25
# This uses the kaminari gem.
# https://github.com/kaminari/kaminari
@sessions = Session.page(params[:page] || 1)
@page_count = @sessions.total_pages
end
def edit
@session = Session.includes(:memberships).includes(:captionists).find(params[:id])
if !@session
raise ActionController::RoutingError.new('Not Found')
end
end
def update
@session = Session.find(params[:id])
if @session.update_attributes(session_params)
respond_to do |format|
format.html { redirect_to edit_admin_session_path , notice: 'Session was successfully updated.' }
end
return
end
render 'edit'
end
def session_params
return params.require(:session).permit(:name, :passcode, :shortcode,
membership_attributes: [:id, :hostname, :tag_teaming, :web_port, :tcp_port, :allowing_connections, :_destroy])
end
end
end
最后但同样重要的是 .erb 文件:
<h1>Editting!!</h1>
<%= link_to 'Go Back To list', admin_sessions_path %>
<%= form_for :session, method: :patch, class:'container' do |f| %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %><br />
</div>
<div class="form-group">
<%= f.label :passcode %>
<%= f.text_field :passcode, class: 'form-control' %><br />
</div>
<%= f.label :shortcode %>
<%= f.text_field :shortcode, class: 'form-control' %><br />
<%= f.label :memberships %>
<table class="table table-striped table-hover">
<thead class="thead-dark">
<tr>
<th scope="col"> Allowing Connections </th>
<th scope="col">Tag Teaming</th>
<th scope="col">Hostname</th>
<th scope="col">Web Port</th>
<th scope="col">TCP Port</th>
<th scope="col">Captionist Name </th>
<th scope="col">Action</th>
</tr>
</thead>
<!-- HERE'S THE PART THAT is giving me the problem -->
<% @session.memberships.map do |membership| %>
<%= f.fields_for membership do |ff| %>
<tr class="nested-fields">
<%= ff.hidden_field :id %>
<td>
<%= ff.label :allowing_connections %>
<%= ff.check_box :allowing_connections %>
</td>
<td>
<%= ff.label :tag_teaming %>
<%= ff.check_box :tag_teaming %>
</td>
<td>
<%= ff.label :hostname %>
<%= ff.text_field :hostname %>
</td>
<td>
<%= ff.label :web_port %>
<%= ff.text_field :web_port %>
</td>
<td>
<%= ff.label :tcp_port %>
<%= ff.text_field :tcp_port %>
</td>
<td>
<%= membership.captionist.full_name %>
</td>
<td>
<%= ff.check_box :_destroy %>
</td>
</tr>
<% end %>
<% end %>
</table>
<%= f.submit 'Update', class: 'btn btn-primary' %>
<% end %>
发送的参数如下:
{"name"=>"Session Correct name", "passcode"=>"A Passcode", "shortcode"=>"A Shortcode", "membership"=>{"id"=>"106", "allowing_connections"=>"0", "tag_teaming"=>"0", "hostname"=>"delete hostname", "web_port"=>"80", "tcp_port"=>"3001", "_destroy"=>"0"}} permitted: false>
以下是我将参数列入白名单的方式:
return params.require(:session).permit(:name, :passcode, :shortcode,
membership_attributes: [:id, :hostname, :tag_teaming, :web_port, :tcp_port, :allowing_connections, :_destroy])
有什么问题?
1) 现在,如果我提交表单,我会在控制台末尾得到:unpermitted_params=["membership"] 2) 我检查过数据库是否已正确迁移。外键设置正确。我可以毫无问题地浏览属性。我所做的是在执行迁移后添加关系 has_many。我不知道我是否需要添加某种迁移才能使更新正常工作。 3) 我试过替换
<%= form_for :session, method: :patch, class:'container' do |f| %>
对于: <%= form_for @session, 方法: :patch, class:'container' do |f| %>
但是我收到一个错误:
Could not find a valid mapping for #
4) 我也尝试删除 <% @session.memberships.map do |membership| %>
,但我不会填充这些字段。我看到的另一个问题是输入的名称都相同。每当我发送要更新的参数时,它只会反映嵌套部分的最后一部分。
这是服务器的响应(params[:session]):
Parameters {"name"=>"Session Correct name", "passcode"=>"A Passcode", "shortcode"=>"A Shortcode", "membership"=>{"id"=>"106", "allowing_connections"=>"0", "tag_teaming"=>"0", "hostname"=>"delete hostname", "web_port"=>"80", "tcp_port"=>"3001", "_destroy"=>"0"}} permitted: false>
这是一个视觉效果示例:
未嵌套的部分得到更新。
有什么想法吗?
谢谢!
编辑(2018 年 4 月 9 日):添加了发送的参数(我这里没有)。修复了格式和 HTML.
的问题我很确定这只是一个复数问题。我正在研究与您的代码类似的代码,看起来您想要 memberships_attributes
在您的强参数中。
还要检查正在生成的 HTML,并确保您允许的密钥匹配。
我建议在控制器中放置一个 binding.pry 或任何你喜欢调试的东西,然后尝试用传入的参数保存对象。
你不需要:
<% @session.memberships.map do |membership| %>
<%= f.fields_for membership do |ff| %>
您只需要:
<%= f.fields_for @session.memberships do |ff| %>
Rails 将遍历会话中的所有成员并将所有字段放入 memberships_attributes.