Why I get Unpermitted parameters error when I try to save with HABTM check_box_tag?

guduf

Ruby version :

ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-linux]

Rails version :

Rails 4.1.4

I have a HABTM association between two models : Product and Page. This association works well when I use the console with the following command :

Product.first.pages << Page.first

I inserted a check_box_tag in my products/_form.html.haml for displaying my pages. The check_box works well and I can check/uncheck all my pages.

The problem is when I try to submit the form, the modifications I have done in the checkbox are not saved. I have this problem in both ways.

I figured the problem is the Unpermitted parameters error in my log.

Started PATCH "/admin/pages/linge-de-lit" for 127.0.0.1 at 2014-09-23 23:13:40 +0200
Processing by Admin::PagesController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"q/ZskL+Zijc8GMX9lF+EPgqc9uic9N9B/isWYHx7Cx0=", "page"=>{"category"=>"fabric", "title"=>"Linge de lit", "intro"=>"", "visibility"=>"1", "favorite"=>"0", "priority"=>"50", "product_ids"=>["", "1", "2"]}, "commit"=>"Save", "id"=>"linge-de-lit"}
  Page Load (0.3ms)  SELECT  "pages".* FROM "pages"  WHERE "pages"."slug" = 'linge-de-lit'  ORDER BY "pages"."id" ASC LIMIT 1
Unpermitted parameters: product_ids
   (0.3ms)  begin transaction
  Page Exists (0.3ms)  SELECT  1 AS one FROM "pages"  WHERE ("pages"."title" = 'Linge de lit' AND "pages"."id" != 5) LIMIT 1
   (0.2ms)  commit transaction
Redirected to http://localhost:3000/admin/pages/linge-de-lit
Completed 302 Found in 86ms (ActiveRecord: 1.1ms)

I think the problem is my strong parameters but I doesn't see where.

Here is my models :

class Product < ActiveRecord::Base
  has_and_belongs_to_many :pages
end

class Page < ActiveRecord::Base
  has_and_belongs_to_many :products
  has_many :posts
  validates :category, presence: true
  validates :title, presence: true, length: {maximum: 20}, uniqueness: true

  extend FriendlyId
  friendly_id :title, :use => [:slugged, :finders]

  before_save :default_values
  def default_values
    self.title = self.title.capitalize
  end

#Scopes
  scope :visible, -> { where(visibility: true) }
  scope :favorite, -> { where(favorite: true) }

end

Here is my controllers :

class Admin::ProductsController < ApplicationController
  before_action :set_product, only: [:show, :edit, :update, :destroy]

  # GET /admin/products
  def index
    @products = Product.all
  end

  # GET /admin/products/1
  def show
  end

  # GET /admin/products/new
  def new
    @product = Product.new
    @pages = Page.all
  end

  # GET /admin/products/1/edit
  def edit
    @pages = Page.all
  end

  # POST /admin/products
  def create
    @product = Product.new(product_params)

    if @product.save
      redirect_to [:admin, @product], notice: 'Product was successfully created.'
    else
      render action: 'new'
    end
  end

  # PATCH/PUT /admin/products/1
  def update
    if @product.update(product_params)
      redirect_to [:admin, @product], notice: 'Product was successfully updated.'
    else
      render action: 'edit'
    end
  end

  # DELETE /admin/products/1
  def destroy
    @product.destroy
    redirect_to admin_products_url, notice: 'Product was successfully destroyed.'
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_product
      @product = Product.find(params[:id])
    end

    # Only allow a trusted parameter "white list" through.
    def product_params
      params.require(:product).permit(:name, :description, :brand_id, :price, :minimum_price, :shop_disponibility, :web_disponibility, :purchase_link, :favorite, {:pages_ids => []})
    end
end

class Admin::PagesController < ApplicationController
  before_action :set_page, only: [:show, :edit, :update, :destroy]

  # GET /admin/pages
  def index
    @pages = Page.all
  end

  # GET /admin/pages/1
  def show
  end

  # GET /admin/pages/new
  def new
    @page = Page.new
    @products = Product.all
  end

  # GET /admin/pages/1/edit
  def edit
    @products = Product.all
  end

  # POST /admin/pages
  def create
    @page = Page.new(page_params)

    if @page.save
      redirect_to [:admin, @page], notice: 'Page was successfully created.'
    else
      render action: 'new'
    end
  end

  # PATCH/PUT /admin/pages/1
  def update
    if @page.update(page_params)
      redirect_to [:admin, @page], notice: 'Page was successfully updated.'
    else
      render action: 'edit'
    end
  end

  # DELETE /admin/pages/1
  def destroy
    @page.destroy
    redirect_to admin_pages_url, notice: 'Page was successfully destroyed.'
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_page
      @page = Page.find(params[:id])
    end

    # Only allow a trusted parameter "white list" through.
    def page_params
      params.require(:page).permit(:category, :title, :intro, :visibility, :favorite, :priority, :products_ids => [])
    end
end

Here is my views :

# products/_form

= form_for([:admin, @product]) do |f|
  - if @product.errors.any?
    #error_explanation
      %h2= "#{pluralize(@product.errors.count, "error")} prohibited this product from being saved:"
      %ul
        - @product.errors.full_messages.each do |msg|
          %li= msg

  .field
    = f.label :name
    = f.text_field :name
  .field
    = f.label :description
    = f.text_area :description
  .field
    = f.label :brand_id
    = f.text_field :brand_id
  .field
    = f.label :price
    = f.text_field :price
  .field
    = f.label :minimum_price
    = f.check_box :minimum_price
  .field
    = f.label :shop_disponibility
    = f.check_box :shop_disponibility
  .field
    = f.label :web_disponibility
    = f.check_box :web_disponibility
  .field
    = f.label :purchase_link
    = f.text_field :purchase_link
  .field
    = f.label :favorite
    = f.check_box :favorite

  = hidden_field_tag "product[page_ids][]", nil
  - Page.all.each do |page|
    = check_box_tag "product[page_ids][]", page.id, @product.page_ids.include?(page.id), id: dom_id(page)
    = label_tag dom_id(page), page.title

  .actions
    = f.submit 'Save'

# pages/_form

= form_for([:admin, @page]) do |f|
  - if @page.errors.any?
    #error_explanation
      %h2= "#{pluralize(@page.errors.count, "error")} prohibited this page from being saved:"
      %ul
        - @page.errors.full_messages.each do |msg|
          %li= msg

  .field
    = f.label :category
    = f.select :category, options_for_select(@pages_categories), {:prompt => "- Sélectionner une catégorie -"}
  .field
    = f.label :title
    = f.text_field :title
  .field
    = f.label :intro
    = f.text_area :intro
  .field
    = f.label :visibility
    = f.check_box :visibility
  .field
    = f.label :favorite
    = f.check_box :favorite
  .field
    = f.label :priority
    = f.number_field :priority

  = hidden_field_tag "page[product_ids][]", nil
  - Product.all.each do |product|
    = check_box_tag "page[product_ids][]", product.id, @page.product_ids.include?(product.id), id: dom_id(product)
    = label_tag dom_id(product), product.name

  .actions
    = f.submit 'Save'
Marcelo Ribeiro

There is a typo there :) Instead of

# Only allow a trusted parameter "white list" through.
def page_params
  params.require(:page).permit(:category, :title, :intro, :visibility, :favorite, :priority, :products_ids => [])
end

It should be product_ids:

# Only allow a trusted parameter "white list" through.
def page_params
  params.require(:page).permit(:category, :title, :intro, :visibility, :favorite, :priority, :product_ids => [])
end

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related