【问题标题】:Updating many to many using a Nested form_for in rails使用 Rails 中的嵌套 form_for 更新多对多
【发布时间】:2018-04-06 21:25:58
【问题描述】:

这是一个很常见的问题,有几十篇博客文章涵盖了这个问题。我只是不知道我做错了什么。 抱歉,帖子太长了。

我有一个Mono-Transitive many-to-many relationship

一个会话可能有多个字幕。字幕员可能有多个会话。多对多关系由“成员”完成。

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_pa​​rams=["membership"] 在控制台的末尾 2) 我检查了数据库是否已正确迁移。外键设置正确。我可以毫无问题地浏览属性。我所做的是在执行迁移后添加关系 has_many 。我不知道是否需要添加某种迁移才能使更新正常工作。 3)我试过替换

<%= form_for :session, method: :patch,  class:'container' do |f| %>

对于:

但我收到一个错误:

找不到#的有效映射

4) 我也尝试删除 &lt;% @session.memberships.map do |membership| %&gt;,但我不会填充这些字段。我看到的另一个问题是输入的名称都是相同的。每当我发送要更新的参数时,它只是被反映的嵌套部分的最后一个。

这是来自服务器的响应(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 的问题。

【问题讨论】:

  • 看起来参数在这里的格式中丢失了:> 这是发送的参数:
  • 不确定这是否与任何事情有关,但我不建议将隐藏字段放在 和 之间。这可能会导致语法错误,并可能在渲染中丢失。
  • @emptywalls:感谢您指出这一点!另外,感谢隐藏字段。我要把它从那里移到桌子外面。
  • @JoseA:实际上,我把它移到了

标签: ruby-on-rails activerecord erb nested-forms form-for


【解决方案1】:

你不需要:

<% @session.memberships.map do |membership| %>

  <%= f.fields_for membership do |ff| %>

你只需要:

<%= f.fields_for @session.memberships do |ff| %>

Rails 将遍历会话中的所有成员,并将所有字段放入 members_attributes。

【讨论】:

  • 谢谢。你看,当我这样做时我遇到了问题。我不知道这是否与 Active Record 有关。但是我所有的字段都是“未定义的”。看到这个:imgur.com/a/0aAV6
  • 查看我对空墙回复的第三条评论 (stackoverflow.com/a/49701382/1057052)
  • 我不确定。我认为您的form_for 使用:session 而您的fields_for 使用@session。不确定这是否会成为问题,但请尝试在这两种情况下使用 @session。您还应该在 Controller 中重命名 memberships_attributes(复数)。此外,您的参数没有收到session 密钥
  • 我认为这可能是 Devise 的问题。你在用吗? Session 可能是一个已经在使用的类。也许您可以尝试重命名您的 Seasion 课程。我只是猜测......
  • 我也指的是类名。能否包含日志中的整个错误消息?
【解决方案2】:

我很确定这只是一个多元化问题。我在这里寻找与您的代码相似的代码,看起来您希望在您的强参数中使用memberships_attributes

还要检查正在生成的 HTML,并确保您允许的键匹配。

我建议在控制器中放置一个 binding.pry 或任何您想调试的东西,并尝试使用传入的参数保存对象。

【讨论】:

  • 再次感谢您的帮助:) 我认为问题可能在于如何生成 HTML 以及如何允许密钥。由于这是一个多维数组,因此生成 HTML 有点棘手。
  • 我还想问一下:由于我是通过each 渲染HTML,所以name 属性会生成一个singular 成员名称:@987654323 @(这是 tcp_port 的)。我已经尝试了几种组合,比如最后添加一个[ ],但我还没有让它工作:session[membership][tcp_port][]
  • 我想提供一个更新:我能够手动生成 HTML 并使其正常工作!我不相信这一点,因为它不是 Rails 方式。我可以在这里发布答案,或者我们可以继续与代码搏斗。归根结底,这是我必须写的名字:session[memberships_attributes][][tcp_port],而 Rails 生成的名字是:session[membership][tcp_port]
猜你喜欢
相关资源
最近更新 更多
热门标签