Unpermitted parameters mimo, że są dozwolone

W projekcie używam Rails 5.1.4 i cancancan 2.0 i mam takie modele:

class Reservation < ApplicationRecord
  has_one :stretch, dependent: :destroy

  has_many :reservation_additional_services, dependent: :destroy
  has_many :additional_services, through: :reservation_additional_services

  belongs_to :user

  enum status: [:submitted, :cancelled, :confirmed, :on_going, :altered, :finished]

  accepts_nested_attributes_for :stretch
  validates :additional_service_ids, presence: true, unless: ->(reservation) { reservation.is_a?(Reservation::Stay) }

  def cancel!
    self.cancelled!
  end

  def costs
    additional_costs + additional_services.map(&:price).inject(0){|sum, x| sum + x}
  end
end

class AdditionalService < ApplicationRecord
  has_many :reservation_additional_services
  has_many :reservations, through: :reservation_additional_services
end

i następujący kontroler:

class ReservationsController < ApplicationController
  load_and_authorize_resource except: [:index, :create]

  def index
    @reservations = current_user.reservations
  end

  def new
    @reservation.build_stretch
  end

  def create
    @reservation = Reservation.new(reservation_prams)
    @reservation.user = current_user

    @reservation.build_stretch unless @reservation.stretch.present?

    if @reservation.save
      redirect_to reservations_url
    else
      render :new, status: :not_acceptable
    end
  end

  def show
  end

  def edit
  end

  def update
    puts "**************************"
    p update_params
    p params
    if @reservation.update_attributes(update_params)
      redirect_to reservations_url
    else
      render :edit, status: :not_acceptable
    end 
  end

  def destroy
    @reservation.cancel!
    redirect_to reservations_url
  end

  private
    def reservation_prams
      params.require(:reservation).permit(
        stretch_attributes: [:start_date, :end_date],
        additional_service_ids: []
      )
    end

    def update_params
      params.require(:reservation).permit(
        additional_service_ids: []
      )
    end
end

oraz taki formularz do tworzenia rezerwacji i wiązania jej z AdditionalServices:

= simple_form_for(@reservation,
  defaults: { label: false }) do |f|
  .row
    = f.simple_fields_for :stretch do |sf|
      = sf.input :start_date, as: :string, label: t('.labels.start_date'), input_html: { class: 'datepicker'  }
      = sf.input :end_date, as: :string, label: t('.labels.end_date'), input_html: { class: 'datepicker' }
    - if @reservation.is_a?(Reservation::Stay)
      = f.input :board, as: :select, label: t('.labels.board'),
        collection: Reservation::Stay.boards.keys.collect {|key| [t("reservation.board.#{key}"), key]}, include_blank: false  
      = f.input :additional_service_ids,
        as: :select, input_html: { multiple: true }, label: t('.labels.additional_service'),
        collection: AdditionalService.all.collect {|as| ["#{as.name} - #{as.price}", as.id] }
      = f.input :room_ids,
        as: :select,
        input_html: { multiple: true },
        label: t('.label.rooms'),
        collection: Room.all.collect{ |room| ["#{room.number}", room.id]}
    - else
      = f.input :additional_service_ids,
          as: :select, label: t('.labels.additional_service'),  
          collection: AdditionalService.all.collect {|as| ["#{as.name} - #{as.price}", as.id] }

  .row
    = f.submit t('.labels.book')

javascript:
  Reservations.initForm();

Przy wykonywaniu akcji create w logach serwera dostaję komunikat: Unpermitted parameters: :additional_service_ids , gdy drukuje params’y i reservation_prams dostaję:

{"utf8"=>"✓", "authenticity_token"=>"eM4Fxg/HNBpjMk/0apmYRRSSy0h41RmuW6wxcgde9yytQkMMdLf1lyLRciBSrybenVKxNYjaa7kRkuMDgGcT0g==", "reservation"=>{"stretch_attributes"=>{"start_date"=>"2017-09-30", "end_date"=>"2017-10-01"}, "additional_service_ids"=>"1"}, "commit"=>"Zarezerwuj", "controller"=>"reservations", "action"=>"create"}
Unpermitted parameter: :additional_service_ids
{"stretch_attributes"=><ActionController::Parameters {"start_date"=>"2017-09-30", "end_date"=>"2017-10-01"} permitted: true>}        

Nie rozumiem czemu additional_service_ids jest unpermitted(ten sam problem występuje też przy akcji update, gdy stworzę rezerwacje z konsoli). Co najlepsze mam kolejny model, który dziedziczy z Reservations i wygląda w ten sposób:

class Reservation::Stay < Reservation
  has_and_belongs_to_many :rooms,
    foreign_key: 'reservation_id',
    association_foreign_key: 'room_id',  
    join_table: :stays_rooms

  validates :room_ids, presence: true
  enum board: [:without, :half, :full]
  
  def costs
    super + rooms.map(&:price).inject(0){ |sum, x| x * (stretch.end_date.to_date - stretch.start_date.to_date).to_i }
  end
end

i taki kontroler:

class StaysController < ApplicationController
  load_and_authorize_resource :reservation, parent: false, class: Reservation::Stay, except: [:create]

  def new
    @reservation.build_stretch
    render '/reservations/new'
  end

  def create
    @reservation = Reservation::Stay.new(stay_params)
    @reservation.user = current_user

    @reservation.build_stretch unless @reservation.stretch.present?    
    
    if @reservation.save
      redirect_to reservations_url
    else
      render '/reservations/new', status: :not_acceptable
    end
  end

  def update
    if @reservation.update_attributes(update_params)
      @reservation.altered!
      redirect_to reservations_url
    else
      render '/reservations/edit', status: :not_acceptable
    end 
  end

  private
    def stay_params
      params.require(:reservation_stay).permit(
        :board,
        stretch_attributes: [:start_date, :end_date],
        additional_service_ids: [],
        room_ids: []
      )
    end

    def update_params
      params.require(:reservation_stay).permit(
        :board,
        additional_service_ids: []
      )
    end
end

i tu wszystko jest już super stay jest tworzony i updateowany bez problemów.

Wygląda na to, że w paramsach jako additional_service_ids przesyłasz pojedynczą wartość (1), a kontroler oczekuje tablicy.

Faktycznie dzięki, nie zauważyłem. W sumie to mam jeszcze jedno pytanie, jak wywołuje load_and_authorize_resource dla akcji create w ReservationsController to dostaję taki błąd:

ActionController::UnfilteredParameters in ReservationsController#create

i w konsoli są dostępne parameters, a nie params:

Po pierwsze w tym kodzie, który wkleiłeś nie jest wywoływane load_and_authorize_resource w akcji create w ReservationsController (jest except: [:index, :create]).

Po drugie, w konsoli nie znajduje params bo szuka w ActionController::Parameters, a nie ActionController.

Wiem, że nie jest zapomniałem napisać, ale chcę to zmienić i po zmianie tzn usunięciu :create z except działa to w powyżej opisany sposób.

Z githuba cancancan:

For the :create action, CanCanCan will try to initialize a new instance with sanitized input by seeing if your controller will respond to the following methods (in order):

  1. create_params
  2. <model_name>_params such as article_params (this is the default convention in rails for naming your param method)
  3. resource_params (a generically named method you could specify in each controller)

Masz literówkę: reservation_prams zamiast reservation_params.

1 Like