Rails 多个嵌套表单

Rails Multiple Nested Forms

这是我的第一个 Rails 项目,我正在尝试创建一个表单来添加可以具有多个 属性 名称和值对的产品。我遵循了几个教程,并在此处查看了几个有关嵌套表单的答案,但我对信息是否实际保存和显示信息感到困惑。我使用 Cocoon gem 动态创建额外的 属性 名称和值对。我尝试使用简单的嵌套形式来确保我正确地构建了模型——这就是下面的代码。我在下面包含了我项目中的相关代码。任何帮助是极大的赞赏!

controllers/products_controller.erb

class ProductsController < ApplicationController
  def index
    @products = Product.all
    gon.products = Product.all
  end

  def show
    @product = Product.find(params[:id])
    @properties = Product.find(params[:id])
  end

  def new
    @product = Product.new
    @properties = @product.properties.build
    @product_properties = @properties.product_properties.build
  end

  def create
    @product = Product.new(product_params)

    if @product.save
    redirect_to products_path,
    notice: 'The product was successfully created.'
    else
      render 'new'
    end
  end

  private
  def product_params
    params.require(:product).permit(:name, :upc, :available_on,
      :properties_attributes => [:property_name,
        :product_properties_attributes => [:value]
        ])
  end
  def get_property
    @property = Property.find(params[:property_id])
  end
end

models/product.rb

class Product < ApplicationRecord
  has_many :properties
  has_many :product_properties,
           :through => :properties
  accepts_nested_attributes_for :properties
  accepts_nested_attributes_for :product_properties
  attr_accessor :properties_attributes,
                :product_properties_attributes

  validates :name, :upc, :available_on, presence: true
  validates :name, :upc, uniqueness: true
  validates :upc, numericality: { only_integer: true }

  validates :name, length: { maximum: 1024,
    too_long: "%{count} characters is the maximum allowed" }


  validate :check_length

  def check_length
    unless upc.size == 10 or upc.size == 12 or upc.size == 13
      errors.add(:upc, "length must be 10, 12, or 13 characters")
    end
  end

  validate :expiration_date_cannot_be_in_the_past

  def expiration_date_cannot_be_in_the_past
    errors.add(:available_on, "must be a future date") if
      !available_on.blank? and available_on < Date.today
  end

end

models/property.rb

class Property < ApplicationRecord
  belongs_to :product
  has_many :product_properties
  accepts_nested_attributes_for :product_properties
  attr_accessor :property_name,
                :value,
                 :product_properties_attributes


  validates :property_name, presence: true
  validates :property_name, uniqueness: true
  validates :property_name, length: { maximum: 255,
    too_long: "%{count} characters is the maximum allowed" }
end

models/product_property.rb

class ProductProperty < ApplicationRecord
  belongs_to :property
  belongs_to :product
  attr_accessor :value

  validates :value, presence: true
  validates :value, length: { maximum: 255,
    too_long: "%{count} characters is the maximum allowed" }

end

views/products/new.html.erb

<h1>New Product</h1>
<%= form_with scope: @product, url: products_path, local: true do |f| %>

<% if @product.errors.any? %>
   <div id="error_explanation">
     <h2>
       <%= pluralize(@product.errors.count, "error") %> prohibited
       this product from being saved:
     </h2>
     <ul>
       <% @product.errors.full_messages.each do |msg| %>
         <li><%= msg %></li>
       <% end %>
     </ul>
   </div>
 <% end %>

  <p>
    <strong>Name</strong><br>
    <%= f.text_field :name %>
  </p>

  <p>
    <strong>UPC</strong><br>
    <%= f.text_field :upc %>
  </p>

  <p>
    <strong>Available On</strong><br>
    <%= f.date_field :available_on %>
  </p>

  <h3>Properties</h3>
  <div id='properties'>
    <%= f.fields_for (:properties) do |property| %>
      <%= property.fields_for (:product_properties) do |product_property| %>
      <p>
        <strong>Property Name</strong><br>
        <%= property.text_field :property_name %>
      </p>

      <p>
        <strong>Property Value</strong><br>
        <%= product_property.text_field :value %>
      </p>
     <% end %>
<% end %>
  </div>


  <p>
    <%= f.submit "Add Product" %>
  </p>
<% end %>

views/products/index.html.erb

<h1>Products</h1>

<%= link_to 'New Product', new_product_path %>

<div id="search"></div>

<table>
  <tr>
    <th>Name</th>
    <th>UPC</th>
    <th>Available On</th>
    <th></th>
  </tr>

  <% @products.each do |product| %>
    <tr>
      <td><%= product.name %></td>
      <td><%= product.upc %></td>
      <td><%= product.available_on %></td>
       <% product.properties.each do |property| %>
       <td><%= property.property_name %></td>
       <% end %>
      <td><%= link_to 'Details', product_path(product) %></td>
    </tr>
  <% end %>
</table>

新的 form_with 结合了以前的 form_forform_tag 一方面使它非常强大,但有时也很混乱。以前简单form_for @product,现在你

form_with model: @product, local: true do |f|

您对 scope: 的使用只是使用 @product 作为前缀,不会实际迭代或使用 @product 来查找关联,这就是为什么我们看不到 property_attributes,也不是隐含的 id 字段,即使您正确地拥有 accepts_nested_attributes_destroy 字段将由 cocoon 的 link_to_remove_association 添加——如果您想要回到动态 add/remove 嵌套项目的能力)。