【问题标题】:Rails 5 inverse_of join table doesn't want to work with deeply nested Cocoon fieldsRails 5 inverse_of 连接表不想使用深度嵌套的 Cocoon 字段
【发布时间】:2020-05-23 17:36:00
【问题描述】:

我正在使用Ruby 2.6.5Rails 5.2.3。我有一个带有 Cocoon 的双重嵌套对象的表单。该表格适用于Preorders。一个Preorder 可以有多个DancersDancers 可以有很多也可以属于很多Groups,所以我设置了一个名为DancerGroup连接表

我使用 Cocoondancers_attributes 添加到Preorder 表单中,并再次在dancers_attributes 字段中添加dancer_groups_attributes

我的问题是,当我尝试使用新的 Dancer 和新的 DancerGroup 关联保存 Preorder 时,我收到以下错误:

  • Dancers dancer groups dancer 不能为空

这显然意味着它试图在dancer 记录有id 之前保存dancer_group 对象。显而易见的答案是,由于我使用的是Rails 5,默认情况下需要belongs_to 关联,并且解决方法是正确使用inverse_ofaccepts_nested_attributes_for 方法让Rails 管理一次保存所有内容。 但对于我的联接表,它不起作用。当我在 DancerGroup 模型中的 belongs_to :dancer 上设置 optional: true 时,我什至得到了同样的错误。

我的 strong 参数如下所示:

private
  def preorder_params
    params.require(:preorder).permit( :event_id,
                                      :dancers_attributes => [
                                        :_destroy,
                                        :id,
                                        :name,
                                        :dancer_groups_attributes => [
                                          :_destroy,
                                          :group_id,
                                          :id ]
                                        ] 
                                      )
  end

我的模型如下所示:

app/models/preorder.rb

class Preorder < ApplicationRecord
  belongs_to :event
  has_many :dancers, dependent: :destroy, inverse_of: :preorder 
  has_many :dancer_groups, through: :dancers, dependent: :destroy

  with_options reject_if: :all_blank, allow_destroy: true do
    accepts_nested_attributes_for :dancers
    accepts_nested_attributes_for :dancer_groups
  end

  validates :dancers, :length => { minimum: 1 }
  ...
end

app/models/dancer.rb

class Dancer < ApplicationRecord
  belongs_to :preorder, counter_cache: true, inverse_of: :dancers
  has_many :dancer_groups, inverse_of: :dancer, dependent: :destroy
  has_many :groups, through: dancer_groups

  with_options reject_if: :all_blank, allow_destroy: true do
    accepts_nested_attributes_for :dancer_groups
  end
  ...
end

app/models/dancer_group.rb

class DancerGroup < ApplicationRecord
  belongs_to :dancer, inverse_of :dancer_groups
  belongs_to :group, inverse_of :dancer_groups
  ...
end

这是我的表单文件的样子

views/admin/preorders/_form.html.haml

...
%fieldset
  %legend Dancers
  #dancers
    - @preorder.dancers.each do |dancer|
      = f.fields_for :dancers, dancer do |ff|
        = render "dancer_fields", f: ff, preorder: @preorder
    = link_to_add_association f, :dancers,
        { "data-association-insertion-node": "#dancers",
          "data-association-insertion-method": "append",
        render_options: { locals: { preorder: @preorder } },
        class: "btn btn-info btn-sm btn-simple float-right" } do 
      = icon "fas", "plus"
      Add Dancer
...

views/admin/preorders/_dancer_fields.html.haml

...
.card.preorder_dancer
  .card-header.row
    .col
      %h4.card-title Dancer Info
    .col-auto
      = link_to_remove_association f, { wrapper_class: "preorder_dancer",
          class: "btn btn-sm btn-icon btn-simple btn-danger" } do 
        = icon "fas", "times"
  .card-body
    .row.pb-3
      .col 
        = f.label :name do 
          Full Name
          %span.text-danger *
        = f.text-field :name, class: "form-control"
    %h4.card-title Dance Groups
    .row{ :id => "dancer_groups_#{f.object.id}" }
      - f.object.dancer_groups.each do |dancer_group|
        = f.fields_for :dancer_groups, dancer_group do |ff|
          = render "dancer_group_fields, f: ff, 
                                  preorder: preorder
    =link_to_add_association f, :dancer_groups,
      { "data-association-insertion-node": "#dancer_groups_#{f.object.id}",
        "data-association-insertion-method": "append",
        render_options:  {locals: { preorder: preorder } },
        class: "btn btn-info btn-sm btn-simple float-right" } do
      =icon "fas", "plus"
      Add Dance Group

views/admin/preorders/_dancer_group_fields.html.haml

.col-lg-6.preorder_dancer_group
  .card.bg-dark
    .card-header.row
      .col
        %h4.card-title Dance Group
      .col.text-right
        =link_to_remove_association f, { wrapper_class: "preorder_dancer_group",
                                    class: "btn btn-sm btn-icon btn-simple btn-danger"} do
          =icon "fas", "times"
    .card-body
      .row
        .col
          =f.label :group_id do
            Group
            %span.text-danger *
          =f.collection_select :group_id,
                               preorder.event.groups,
                               :id,
                               :name,
                               {include_blank: "Select Group"},
                               {class: "form-control"}

【问题讨论】:

  • Dancer 模型中,您有has_many :dancer_groups, through: :dancers 和afaik,应该只是has_many :dancer_groups,对吧?我希望这也能解决您的问题?
  • @nathanvda 它应该说 inverse_of: 而不是 through: 修复它。
  • 您说“已解决”,所以这是否解决了问题?还是您只是纠正了错字?因为这可以解释错误恕我直言。
  • 更正了错字。没有解决问题。

标签: ruby-on-rails ruby join cocoon-gem


【解决方案1】:

在你的_dancer_fields部分你写

%h4.card-title Dance Groups
.row{ :id => "dancer_groups_#{f.object.id}" }
  - f.object.dancer_groups.each do |dancer_group|
    = f.fields_for :dancer_groups, dancer_group do |ff|
      = render "dancer_group_fields, f: ff, 
                              preorder: preorder

应该是这样的

%h4.card-title Dance Groups
.row{ :id => "dancer_groups_#{f.object.id}" }
  = f.fields_for :dancer_groups do |ff|
    = render "dancer_group_fields, f: ff, 
                              preorder: preorder

因为否则您不会使舞者组“连接”到舞者。

【讨论】:

  • 我刚试过这个。我已经从_dancer_fields.html.haml 中删除了-f.object.dancer_groups.each 行。它没有用。我还从_form.html.haml 中删除了- @preorder.dancers.each do |dancer| 行,两者都没有解决问题。
  • 嗯,很奇怪。你有一个最小的 github repo 可以重现这个问题,所以我可以检查一下?
  • 我不知道,恐怕要过几个星期才能把一个放在一起。
猜你喜欢
  • 1970-01-01
  • 2013-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-04
  • 1970-01-01
相关资源
最近更新 更多