最佳实践方法 - 在1 form_for

时间:2015-06-18 13:46:41

标签: ruby-on-rails ruby 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/show页面的1个表单中,对现有品牌记录创建2条记录(产品和投票)。

我的解决方案:

品牌/ 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

这是解决我的任务的正确方法吗?我能这样用吗?

2 个答案:

答案 0 :(得分:1)

这就是我重构你的create方法的方法:

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

你显然必须相应地更新你的强力参数,但这很容易; - )

答案 1 :(得分:0)

我改变了以下逻辑:

@review.user_id = @vote.user_id = current_user.id
@review.server_id = @vote.server_id = params[:server_id]

只需将current_user.id和params [:server_id]分别添加到product_params和votes_params。

此外,我们还没有看到使用实例变量进行投票/审核的必要性。

除此之外,保存两种型号对我来说似乎没问题。