如何使用Rails 5中的form_tag / fields_tag执行批量更新?

时间:2017-11-30 02:02:56

标签: ruby-on-rails ruby-on-rails-5

创建销售后,将为现有的每个产品创建单独的SaleItem。 SaleItem是唯一引用销售或产品的模型。

我希望能够直接从每个Sale#show中查看和更新​​SaleItem discount_percentage(decimal)和include_in_sale(boolean)属性。

我认为最好将表单指向POST到SaleItem #index而不是编辑,所以我不必担心:id并且可以在完成所有更新后重定向页面。

我从this tutorial.

中获取了form_tag结构的基本概念

非常感谢任何使用最少数量的数据库来实现此功能的帮助!

修改1:更新了强参数以允许未允许的参数。

问题

服务器日志显示以下内容导致回滚:

未经许可的参数:: 0,:1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13 ,:14,:15,:16,:17,:18,:19,:20,:21,:22,:23,:24,:25,:26,:27,:28,:29,: 30,:31

参数值也是字符串而不是整数。见link.

模型关联

出售

has_many :sale_items
has_many :products, :through => :sale_items

促销商品

belongs_to :sale, dependent: :destroy
belongs_to :product

产品

has_many :sale_items

控制器

销售

def show
 @sale_items = @sale.sale_items
end

SaleItems

before_action :sale_items_batch_update, only: [:index]
include BatchUpdate

def index
 # not relevant as data is viewed from sales#show
 # just wanted to show method because this is where form_tag is pointing to.
 @sale_items = SaleItem.all
end

private

def sale_item_params
  params.require(:sale_item).permit(:product_id, :sale_id, :discount_percentage, :include_in_sale).tap do |whitelisted|
    whitelisted['sale_item'] = params[:sale_item]['sale_item']
  end
end

的关注

batch_update.rb

module BatchUpdate

 private

 def sale_items_batch_update
  unless params['sale_item'].nil?
   sale_items = params['sale_item']

   sale_items.each do |key, value|
     # not sure yet
   end
  end
    # redirect_to #original sales/show.html.erb page
 end
end

查看

销售/ show.html.erb

<%= form_tag sale_items_path do %>

  <% @sale_items.each_with_index do |item, index| %>
    <%= fields_for 'sale_item[]', item, include_id: false do |field| %>

      <%= field.hidden_field :id, value: item.id, name: "sale_item[#{index}][id]" %>
      <%= field.hidden_field :product_id, value: item.product_id, name: "sale_item[#{index}][product_id]" %>
      <%= field.hidden_field :sale_id, value: item.sale_id, name: "sale_item[#{index}][sale_id]" %>

      <%= field.check_box :include_in_sale, value: item.include_in_sale, name: "sale_item[#{index}][include_in_sale]" %>
      <%= field.text_field :discount_percentage, value: item.discount_percentage, name: "sale_item[#{index}][discount_percentage]" %> 

    <% end %>
  <% end %>
  <%= submit_tag 'Update All' %>

<% end %>

1 个答案:

答案 0 :(得分:0)

所以我至少在这一点上得到了它。我将进行一些重大的重构,以便以后提高数据库效率。

代码I已删除

batch_update.rb关注&amp; SaleItems控制器中对它的任何引用

更新的代码

视图/销售/显示

<%= form_tag batch_update_sale_items_path, method: :put do %>

  <% @sale_items.each_with_index do |item, index| %>
    <%= fields_for 'sale_item[]', item, include_id: false do |field| %>
      <%= field.hidden_field :id, value: item.id, name: "sale_item[#{index}][id]" %>
      <%= field.check_box :include_in_sale, value: item.include_in_sale, name: "sale_item[#{index}][include_in_sale]" %>
      <%= field.text_field :discount_percentage, value: item.discount_percentage, name: "sale_item[#{index}][discount_percentage]" %> 
    <% end %>
  <% end %>

<%= submit_tag 'Update All' %>
<% end %>

的routes.rb

put 'sales/:id/batch_update_sale_items', to: 'sale_items#batch_update_sale_items', as: 'batch_update_sale_items'

SaleItemsController

# path from Sale#show
def batch_update_sale_items
  @sale = Sale.find(params[:id])

  unless params['sale_item'].nil?
    sale_items_to_update = make_batch_update_hash_pretty(params['sale_item'])
    update_each_sale_item(sale_items_to_update)
  end

  redirect_to sale_path(@sale)
end

private

# takes params['sale_item'] from Sale#show and converts it into a more usable array of hashes
def make_batch_update_hash_pretty( batch_update_hash )
  # convert params to hash & removes indexing keys
  old_hash_array = (batch_update_hash.to_unsafe_h).values
  # converts hash keys to_sym
  old_hash_array = old_hash_array.map(&:symbolize_keys)

  new_hash_array = []
  old_hash_array.map do |hash|
    hash[:id] = hash[:id].to_i
    hash[:include_in_sale] = hash[:include_in_sale].to_i
    hash[:discount_percentage] = hash[:discount_percentage].to_f

    # pushes updated values/types to new hash
    new_hash_array << hash
  end

  return new_hash_array
end

# this method will update a batch of sale_items if their attributes have changed
def update_each_sale_item( sale_items )

  sale_items.each do |item|

    @original_sale_item = SaleItem.find(item[:id])

    # determine if sale_item attributes have been altered
    if @original_sale_item.discount_percentage.to_f != item[:discount_percentage]
      sale_item_attributes_changed = true
    elsif @original_sale_item.include_in_sale != item[:include_in_sale]
      sale_item_attributes_changed = true
    else
      sale_item_attributes_changed = false
    end

    # if true updates attribute
    if sale_item_attributes_changed
      @original_sale_item.update(include_in_sale: item[:include_in_sale], discount_percentage: item[:discount_percentage])
    end

  end
end