最佳实践方法 - 在 1 form_for 中创建 2 条记录
Best practice method - creating 2 records in 1 form_for
我的模特:
brand.rb
has_many :products
has_many :votes
belongs_to :user
accepts_nested_attributes_for :products, :allow_destroy => true
product.rb
belongs_to :user
belongs_to :brand
vote.rb
belongs_to :brand
belongs_to :user
routes.rb
resources :brands do
resources :products
end
我的目标: 在 Brand 的现有 Brand 记录上创建 2 条记录(产品和投票),brand/show
页。
我的解决方案:
brand/show.html.erb
<% form_for([@brand, @brand.send(:product).klass.new]) do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :price %>
<%= f.text_field :price %>
<%= fields_for :votes, @brand.votes.new do |builder| %>
<%= builder.label :rating %>
<%= builder.text_field :rating %>
<% end %>
<%= f.submit %>
<% end %>
products_controller.rb
def create
if Brand.exists?(:id => params[:brand_id])
@review = Review.new(review_params)
@vote = Vote.new(votes_params)
@review.user_id = @vote.user_id = current_user.id
@review.brand_id = @vote.brands_id = params[:brand_id]
if @vote.valid? && @review.valid?
@vote.save
@review.save
redirect_to brands_path
else
flash[:errors][:vote] = @vote.errors
flash[:errors][:review] = @review.errors
redirect_to brands_path
end
end
end
private
def product_params
params.require(:review).permit(:title, :price)
end
def votes_params
params.require(:votes).permit(:rating)
end
这是解决我的任务的正确方法吗?我可以这样使用吗?
我将更改以下逻辑:
@review.user_id = @vote.user_id = current_user.id
@review.server_id = @vote.server_id = params[:server_id]
只需将 current_user.id 和参数 [:server_id] 分别添加到 product_params 和 votes_params。
此外,没有必要为 vote/review 使用实例变量。
除此之外,保存两个模型对我来说似乎还可以。
这就是我重构您的创建方法的方式:
def create
brand = Brand.find(params[:brand_id]) # no test to know if Brand exists, if it does not it means the user gave a wrong Brand id, then a 404 error should be rendered
@product = brand.products.create(products_params.merge({user_id: current_user.id}) # we can directly create this instance
@vote = brand.votes.create(votes_params) # we can directly create this instance
# we add the errors in the flash if they exist
flash[:errors][:vote] = @vote.errors if @vote.errors.present?
flash[:errors][:product] = @product.errors if @product.errors.present?
redirect_to brands_path # since we want to redirect to brands_path if the creation succeeded or failed, we don't need to use it twice in the code
end
另外,小改进:
@brand.send(:product).klass.new
# can become
@brand.products.new # produces a initialized Product instance
fields_for :votes, @brand.votes.new
# can become
f.fields_for @brand.votes.new
# notice the usage of `f` builder to generate the fields_for
# it will nest the params in `params[:brand][:votes_attributes][0]`
# brand.rb
accepts_nested_attributes_for :products, :allow_destroy => true
# add the following:
accepts_nested_attributes_for :votes, :allow_destroy => true
您显然必须相应地更新强参数,但这是简单的部分 ;-)
我的模特:
brand.rb
has_many :products
has_many :votes
belongs_to :user
accepts_nested_attributes_for :products, :allow_destroy => true
product.rb
belongs_to :user
belongs_to :brand
vote.rb
belongs_to :brand
belongs_to :user
routes.rb
resources :brands do
resources :products
end
我的目标: 在 Brand 的现有 Brand 记录上创建 2 条记录(产品和投票),brand/show
页。
我的解决方案:
brand/show.html.erb
<% form_for([@brand, @brand.send(:product).klass.new]) do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :price %>
<%= f.text_field :price %>
<%= fields_for :votes, @brand.votes.new do |builder| %>
<%= builder.label :rating %>
<%= builder.text_field :rating %>
<% end %>
<%= f.submit %>
<% end %>
products_controller.rb
def create
if Brand.exists?(:id => params[:brand_id])
@review = Review.new(review_params)
@vote = Vote.new(votes_params)
@review.user_id = @vote.user_id = current_user.id
@review.brand_id = @vote.brands_id = params[:brand_id]
if @vote.valid? && @review.valid?
@vote.save
@review.save
redirect_to brands_path
else
flash[:errors][:vote] = @vote.errors
flash[:errors][:review] = @review.errors
redirect_to brands_path
end
end
end
private
def product_params
params.require(:review).permit(:title, :price)
end
def votes_params
params.require(:votes).permit(:rating)
end
这是解决我的任务的正确方法吗?我可以这样使用吗?
我将更改以下逻辑:
@review.user_id = @vote.user_id = current_user.id
@review.server_id = @vote.server_id = params[:server_id]
只需将 current_user.id 和参数 [:server_id] 分别添加到 product_params 和 votes_params。
此外,没有必要为 vote/review 使用实例变量。
除此之外,保存两个模型对我来说似乎还可以。
这就是我重构您的创建方法的方式:
def create
brand = Brand.find(params[:brand_id]) # no test to know if Brand exists, if it does not it means the user gave a wrong Brand id, then a 404 error should be rendered
@product = brand.products.create(products_params.merge({user_id: current_user.id}) # we can directly create this instance
@vote = brand.votes.create(votes_params) # we can directly create this instance
# we add the errors in the flash if they exist
flash[:errors][:vote] = @vote.errors if @vote.errors.present?
flash[:errors][:product] = @product.errors if @product.errors.present?
redirect_to brands_path # since we want to redirect to brands_path if the creation succeeded or failed, we don't need to use it twice in the code
end
另外,小改进:
@brand.send(:product).klass.new
# can become
@brand.products.new # produces a initialized Product instance
fields_for :votes, @brand.votes.new
# can become
f.fields_for @brand.votes.new
# notice the usage of `f` builder to generate the fields_for
# it will nest the params in `params[:brand][:votes_attributes][0]`
# brand.rb
accepts_nested_attributes_for :products, :allow_destroy => true
# add the following:
accepts_nested_attributes_for :votes, :allow_destroy => true
您显然必须相应地更新强参数,但这是简单的部分 ;-)