首页 > 解决方案 > 重复表单字段并更新到数据库

问题描述

任何帮助将不胜感激,我对 Rails 相当陌生。

我有两个模型一个购物清单和一个产品。我想一次将多个产品保存/更新到购物清单。

建议的更改不会更新模型。我一直在谷歌搜索,答案是“attr_accessor”还是 find_or_create_by?

尝试 1 - 现有代码

错误

> unknown attribute 'products_attributes' for Product.
Request
Parameters:

{"_method"=>"patch",
 "authenticity_token"=>"3BgTQth38d5ykd3EHiuV1hkUqBZaTmedaJai3p9AR1N2bPlHraVANaxxe5lQYaVcWNoydA3Hb3ooMZxx15YnOQ==",
 "list"=>
  {"products_attributes"=>
    {"0"=>{"title"=>"ten", "id"=>"12"},
     "1"=>{"title"=>"two", "id"=>"13"},
     "2"=>{"title"=>"three", "id"=>"14"},
     "3"=>{"title"=>"four", "id"=>"15"},
     "4"=>{"title"=>"five", "id"=>"16"},
     "5"=>{"title"=>""},
     "6"=>{"title"=>""},
     "7"=>{"title"=>""},
     "8"=>{"title"=>""},
     "9"=>{"title"=>""},
     "10"=>{"title"=>""}}},
 "commit"=>"Save Products",
 "id"=>"7"}

尝试 2 - 页面重新加载没有错误,并且没有更新任何预期的字段。认真地,我正在谷歌搜索并复制和粘贴代码片段,但徒劳地希望解锁正确的组合。添加到产品模式

    class Product < ApplicationRecord
      attr_accessor :products_attributes
      belongs_to :list, optional: true
    end

<%= content_tag(:h1, 'Add Products To This List') %>
<%= form_for(@list) do |f| %>
  <%= f.fields_for :products do |pf| %>
    <%= pf.text_field :title %><br>
  <% end %>
  <p>
    <%= submit_tag "Save Products" %>
  </p>
<% end %>
<%= link_to "Back To List", lists_path %>

列表控制器

    def update
        #render plain: params[:list].inspect
        @list = List.find(params[:id])
        if @list.products.update(params.require(:list).permit(:id, products_attributes: [:id, :title]))
            redirect_to list_path(@list)
        else
            render 'show'
        end

列表模型

    class List < ApplicationRecord
        has_many :products
        accepts_nested_attributes_for :products
    end

原创无所事事——产品模型

    class Product < ApplicationRecord
      belongs_to :list, optional: true
    end

标签: ruby-on-rails

解决方案


如果您只希望用户能够选择产品并将它们放在列表中,则需要多对多关联:

class List < ApplicationRecord
  has_many :list_items
  has_many :products, through: :list_products
end

class ListItem < ApplicationRecord
  belongs_to :list
  belongs_to :product
end

class Product < ApplicationRecord
  has_many :list_items
  has_many :lists, through: :list_products
end

这避免了在 products 表上创建大量重复项,称为规范化

然后,您可以通过简单地使用选择来选择现有产品:

<%= form_for(@list) do |f| %>
  <%= f.label :product_ids %>
  <%= f.collection_select(:product_ids, Product.all, :name, :id) %>
  # ...
<% end %>

请注意,这与嵌套路由或嵌套属性无关。它只是一个使用product_ids关联创建的设置器的选择。此表格仍将提交至/lists/lists/:id

您可以通过以下方式将一组 id 列入白名单:

def list_params
  params.require(:list)
        .permit(:foo, :bar, product_ids: [])
end

要以一种形式添加创建/更新/删除一堆嵌套记录,您可以accepts_nested_attributes_for使用fields_for

class List < ApplicationRecord
  has_many :list_items
  has_many :products, through: :list_products
  accepts_nested_attributes_for :products
end

<%= form_for(@list) do |f| %>
  <%= form.fields_for :products do |pf| %>
    <%= pf.label :title %><br>
    <%= pf.text_field :title %>
  <% end %>
  # ...
<% end %>

如果您不使用记录播种关联,当然fields_for不会显示任何内容。那就是你完全放错地方的那个循环进来的地方。

class ListsController < ApplicationController
  # ...
  def new
    @list = List.new
    5.times { @list.products.new } # seeds the form 
  end

  def edit
    @list = List.find(params[:id])
    5.times { @list.products.new } # seeds the form
  end
  # ...

  def update
    @list = List.find(params[:id])
    if @list.update(list_params)
      redirect_to @list
    else
      render :new
    end
  end

  private
  def list_params
    params.require(:list)
          .permit(
            :foo, :bar,
            product_ids: [],
            products_attrbutes: [ :title ]
          )
  end
end

必读:


推荐阅读