嵌套资源/专家-缺少参数或值为空:

Nested resources / pundit - param is missing or the value is empty:

我这两天遇到了很大的麻烦,需要你的帮助!!我有 2 个模型:花园和预订(使用嵌套资源和使用专家)。在花园 "show" 中,我想单击一个按钮,直接从已创建的花园中 "create" 进行预订。但是我没有成功创建始终显示的操作:缺少参数或值为空:预订。 如何使 "booking" 出现在参数中并能够调用例如 @booking.booker ??你有没有删除 require(:booking) 的解决方案?

我尝试了有或没有 "new_booking" 页面,但没有成功。

非常感谢!!

booking_controller

class BookingsController < ApplicationController
  def index
  end

  def my_bookings
    @bookings = BookingPolicy::Scope.new(current_user, Booking).scope.where(user: current_user)
  end

  def show
    @booking.garden = @garden
  end

  def new
    @garden = Garden.find(params[:garden_id])
    @garden_id = @garden.id
    @booker_id = current_user.id
    @booking = Booking.new
    authorize @booking

  end

  def create
    @booking = Booking.new(booking_params)
    authorize @booking
    @booking.save
    redirect_to root_path
  end



  private

  def booking_params
    params.require(:booking).permit(:garden_id, :booker_id)
  end
end

gardens_controller

class GardensController < ApplicationController
  before_action :set_garden, only: [:show, :destroy, :edit, :update ]
  def new
    @garden = Garden.new
    authorize @garden
  end

  def create
    @garden = Garden.new(garden_params)
    authorize @garden
    @garden.user = current_user

    respond_to do |format|
      if @garden.save
        format.html { redirect_to @garden, notice: 'Garden was successfully created.' }
        format.json { render :show, status: :created, location: @garden }
      else
        format.html { render :new }
        format.json { render json: @garden.errors, status: :unprocessable_entity }
      end
    end
  end

  def index
      @gardens = policy_scope(Garden)
  end

  def my_gardens
    @gardens = GardenPolicy::Scope.new(current_user, Garden).scope.where(user: current_user)

  end

  def show
    authorize @garden
  end

  def destroy
    authorize @garden

    @garden.destroy
    respond_to do |format|
      format.html { redirect_to gardens_url, notice: 'garden was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  def edit
    authorize @garden
  end

 def update
  authorize @garden

  respond_to do |format|
    if @garden.update(garden_params)
      format.html { redirect_to @garden, notice: 'Garden was successfully updated.' }
      format.json { render :show, status: :ok, location: @garden }
    else
      format.html { render :edit }
      format.json { render json: @garden.errors, status: :unprocessable_entity }
    end
  end
end

  private

  def garden_params
    params.require(:garden).permit(:title, :details, :surface, :address, :availabilities)
  end

  def set_garden
    @garden = Garden.find(params[:id])
  end
end

application_controller

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :authenticate_user!
include Pundit

  # Pundit: white-list approach.
  after_action :verify_authorized, except: [:index, :my_gardens], unless: :skip_pundit?
#  after_action :verify_policy_scoped, only: [:index, :my_gardens], unless: :skip_pundit?

  # Uncomment when you *really understand* Pundit!
  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
  def user_not_authorized
    flash[:alert] = "You are not authorized to perform this action."
    redirect_to(root_path)
  end

  private

  def skip_pundit?
    devise_controller? || params[:controller] =~ /(^(rails_)?admin)|(^pages$)/
  end
end

pages_controller

class PagesController < ApplicationController
  skip_before_action :authenticate_user!, only: [:home]

  def home
  end
end

application_record.rb

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end

booking.rb

class Booking < ApplicationRecord
  belongs_to :gardens
  has_many :users, through: :gardens
end

garden.rb

class Garden < ApplicationRecord
  belongs_to :user
  has_many :bookings
end

user.rb

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  has_many :gardens
  has_many :bookings, through: :gardens
end

application_policy.rb

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record
  end

  def index?
    false
  end

  def my_gardens?
    false
  end

  def show?
    scope.where(:id => record.id).exists?
  end

  def create?
    false
  end

  def new?
    create?
  end

  def update?
    false
  end

  def edit?
    update?
  end

  def destroy?
    false
  end

  def scope
    Pundit.policy_scope!(user, record.class)
  end

  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      @user = user
      @scope = scope
    end

    def resolve
      scope
    end
  end
end

booking_policy.rb

class BookingPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
      scope.all
    end
  end

  def create?
    true
  end

  def update?
    user_is_the_owner_or_admin?
  end

  def destroy?
    user_is_the_owner_or_admin?
  end

  def index?
    true
  end

  def show
    scope.where(:id => record.id).exists?
  end

  private

  def user_is_the_owner_or_admin?
    user.admin || record.booker == user
  end
end

garden_policy.rb

class GardenPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
        scope.all #>> permet d'afficher tous les restos dans index
    end
  end

  def create?
    true
  end

  def update?
    user_is_the_owner_or_admin?
  end

  def destroy?
    user_is_the_owner_or_admin?
  end

  def index?
    true
  end

  def show
    scope.where(:id => record.id).exists?
  end

  private

  def user_is_the_owner_or_admin?
    user.admin || record.user == user
  end
end

new_booking.html.erb

<h1>My booking</h1>
<%= simple_form_for [@garden, @booking] do |f| %>
<%= f.input :booker_id, disabled:true %>
<%= f.input :garden_id, disabled:true %>
<%= f.button :submit %>
<% end %>

show_garden.html.erb

<h1>My Garden</h1>
<ul>
<li>Title : <%= @garden.title  %></li>
<li>Address : <%= @garden.address %></li>
<li>Details : <%= @garden.details %></li>
<li>Surface : <%= @garden.surface %></li>
<p> Here will appear the availabilities to select</p>
<% if policy(@garden).destroy? %>
  <li><%= link_to "Delete" %> //
<% end %>
<% if policy(@garden).edit? %>
  <%= link_to "Edit" %></li>
<% end %>
<li><%= link_to "home", gardens_path %></li>
<li><%= link_to "My gardens", my_gardens_path %></li>
<li><%= link_to "Book the Garden", new_garden_booking_path(@garden) %></li> 
</ul>

routes.rb

Rails.application.routes.draw do
  devise_for :users
  delete 'gardens/:id', to: "gardens#destroy", as: :delete_garden
  resources :gardens, except: [:delete] do
    resources :bookings, only: [:new, :create, :show]
  end
  get "my_gardens", to: "gardens#my_gardens", as: :my_gardens
  root to: 'pages#home'
end

But I don't succeed with the create action which always displays : param is missing or the value is empty: booking

Rails 中的强参数是嵌套哈希。 :booking 需要 环绕 预订对象的参数。参数名称以 POST 到服务器的形式定义。

<h1>My booking</h1>
<%= simple_form_for [@garden, @booking] do |f| %>
<%= f.input 'booking[booker_id]', disabled:true %>
<%= f.input :garden_id, disabled:true %>
<%= f.button :submit %>
<% end %>

在Rails中,有很多方法可以做到这一点。你这样做可能更好:

您可以使用 fields_for 助手:

= form_tag action_path do

  = fields_for :model_a do |a|
    = a.text_field :name

  = fields_for :model_b do |b|
    = b.text_field :name

  = submit_tag 'Submit'

问题在于您创建预订的方式。我有几个建议。首先,您不需要 form 来创建 booking,因为您只是传递值,您可以使用 link_toPOST 方法来代替。

首先更新你的路线:

# ...
resources :gardens, except: [:delete] do
   resource :booking, only: :create  # notice resource is singular
end

预订花园link更改为:

<%= link_to "Book the Garden", garden_booking_path(@garden), method: :post %>

为了能够调用 booking.booker,您需要更新您的 Booking 模型关联:

class Booking < ApplicationRecord
  belongs_to :garden  # notice garden is singular
  belongs_to :booker, class_name: "User"
end

然后更新您的 BookingsController 创建操作:

def create
  @garden  = Garden.find params[:garden_id]
  @booking = @garden.bookings.build(booker: current_user)

  # ... your pundit authorizations

  if @booking.save
    redirect_to @garden
  end
end

请注意,在创建预订后,控制器将重定向到花园。这将允许您出示预订。我认为这种方式更好,但如果您愿意,可以重定向到 root_path