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_for
和 form_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 嵌套项目的能力)。
这是我的第一个 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_for
和 form_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 嵌套项目的能力)。