Search code examples
ruby-on-railscontrollerargument-error

Why does my rails update method using the same strong params as my create method, give me an argument error?


I am using Rails 5.2 and building an ecomm site.

My new.html.erb page has a simple_form with a combination of Product fields as well as other params which are handled outside the strong params and the creation of the new instance.

One of the features of the form is a eventListener which creates the :sku automatically based on four input values. The product number, the sku and the live status are hidden.

Here is the simplified version of the html.erb:

<%= simple_form_for @new_product do |f| %>
<%= f.error_notification %>

  <%= f.input :name%>

  <%= f.association :size, collection: @all_sizes.map{ |s| [s.name, s.id, {'data-code-num' => s.code_num}]}, input_html:{class: 'sku-suffix-component'} %>
  <%= f.association :color, collection: @all_colors.map{ |s| [s.name, s.id, {'data-code-num' => s.code_num}]}, input_html:{class: 'sku-suffix-component'} %>
  <%= f.association :pattern, collection: @all_patterns.map{ |s| [s.name, s.id, {'data-code-num' => s.code_num}]}, input_html:{class: 'sku-suffix-component'} %>
  <%= f.input :product_number, as: :hidden, input_html:{ value: @new_product.product_number, class: "sku-suffix-component" } %>
  <%= f.input :sku,as: :hidden, html:{id: "new-product-form-sku-input"} %>

  <%= f.input :live_status, as: :hidden, :input_html => { :value => @new_product.live_status } %>
  <%= f.input :description, as: :text %>
  <%= f.association :brand%>
  <%= f.association :style %>
  <%= f.input :price %>
  <%= f.input :quantity, input_html:{value: 1}%>
  <%= f.association :segment %>
  <%= f.association :main_category %>
  <%= f.association :category %>
  <%= f.association :country, class: '' %>

  <!-- Here are some inputs for adding records to a material join table -->
  <!-- And the names of the inputs are dynamically created -->
  <% 5.times.with_index do |_, i| %>
    <% num = i + 1 %>
    <label class="form-control-label integer" for="material_percent_id_<%= num < 10 ? "0" + num.to_s : num.to_s %>">Percent</label>
    <input class="form-control numeric integer required" type="number" step="1" name="material_percent_<%= num < 10 ? "0" + num.to_s : num.to_s %>" id="material_percent_id_<%= num < 10 ? "0" + num.to_s : num.to_s %>">

    <label class="form-control-label select" for="material_id_id_<%= num < 10 ? "0" + num.to_s : num.to_s %>">Material Component #<%= num %> </label>
    <select class="form-control select" name="material_id_<%= num < 10 ? "0" + num.to_s : num.to_s %>" id="material_id_id_<%= num < 10 ? "0" + num.to_s : num.to_s %>">
      <option value=""></option>
      <% @all_materials.each do |material| %>
        <option value="<%= material.id %>"><%= material.name %></option>
      <% end %>
    </select>
  <% end %>

  <!-- Here are some inputs for adding multiple photos to the products using active_storage -->
  <% (1..8).each do |i| %>
      <%= f.label "Photo [#{i}]"  %>
      <%= f.file_field :photos, multiple: true %>
  <% end %>

  <%= f.button :submit %>

<% end %>

Creating new products using this simple_form works fine, along with instances in the join table, through the create method, shown here:

def create
  @all_sizes = Size.all
  @all_colors = Color.all
  @all_patterns = Pattern.all
  @new_product = Product.new(product_params)
  @all_materials = Material.all

  if @new_product.save
    5.times.with_index do |_, i|
      if params["material_id_0#{(i + 1)}"] != ""
        ProductMaterial.create!(product_id: @new_product.id,
                                material_id: params["material_id_0#{(i + 1)}"].to_i,
                                percent: params["material_percent_0#{(i + 1)}"].to_i)
      else
        break
      end
    end
    redirect_to @new_product
  else
    render :new
  end
end

Using nearly the exact same form (some code added to dynamically render the join table inputs and the photos inputs correctly), but all inputs present; and using the exact same strong params in the controller; going through the update method in the controller produces an argument error. Here is the update method:

  def update
    @product = Product.find(params[:id])

    @product = Product.update(product_params) # here is where the error happens
    @all_sizes = Size.all
    @all_colors = Color.all
    @all_patterns = Pattern.all
    @all_materials = Material.all

    if @product.save
      5.times.with_index do |_, i|
        if params["material_id_0#{(i + 1)}"] != ""
          ProductMaterial.create!(product_id: @product.id,
                                  material_id: params["material_id_0#{(i + 1)}"].to_i,
                                  percent: params["material_percent_0#{(i + 1)}"].to_i)
        else
          break
        end
      end
      redirect_to @product
    else
      render :edit
    end
  end

Here is the exact syntax of the error as seen in the server:

ArgumentError (wrong number of arguments (given 1, expected 2)):

app/controllers/products_controller.rb:72:in `update'

enter image description here


Solution

  • Here

    @product = Product.update(product_params)
    

    you're trying to call instance method update on the Product class itself. new is a class method, so it works good in create action. How it should be:

    def update
      @product = Product.find(params[:id])
      # here you define different @all instances
    
      # you don't need to update and save separately, because instance is saved already 
      # if you call update on it and update goes well
      if @product.update(product_params) 
        # here goes the rest controller code