Ruby on rails Pundit在调用authorize对象的循环中引发AuthorizationNotPerformedError

Ruby on rails Pundit在调用authorize对象的循环中引发AuthorizationNotPerformedError,ruby-on-rails,authorization,pundit,Ruby On Rails,Authorization,Pundit,我有两个rails应用程序: 一个是“前端应用程序”,负责显示数据、从用户获取输入并将数据发送到API(第二个应用程序)。第二个是处理数据库操作和向前端应用程序发送JSON的API 我有一个操作,在该操作中,我的用户决定他想在他的酒店中创建多少房间,以及他应该在每个房间中创建多少张床。前端应用程序上的表单如下所示: <h1>Add Rooms</h1> <form action="http://localhost:3000/hotels/<%=params[:

我有两个rails应用程序: 一个是“前端应用程序”,负责显示数据、从用户获取输入并将数据发送到API(第二个应用程序)。第二个是处理数据库操作和向前端应用程序发送JSON的API

我有一个操作,在该操作中,我的用户决定他想在他的
酒店中创建多少
房间
,以及他应该在每个
房间
中创建多少张
床。前端应用程序上的表单如下所示:

<h1>Add Rooms</h1>
<form action="http://localhost:3000/hotels/<%=params[:hotel_id]%>/rooms/multiple_create" method="post">
  <input name="authenticity_token" value="<%= form_authenticity_token %>" type="hidden">
  <div class="form-group">
    <%= label_tag 'room[room_number]', "Number of rooms" %>
    <input type="number" name="room[room_number]" required>
  </div>
  <div class="form-group">
    <%= label_tag "room[bed_number]", "Number of beds by room" %>
    <input type="number" name= "room[bed_number]" required>
  </div>
  <div class="form-group">
    <%= label_tag "room[content]", "Room Type" %>
    <input type="text" name= "room[content]" required>
  </div>
  <input type="submit">
    <% if @errors %>
    <ul class="list-unstyled">
      <%@errors.each do |error|%>
        <li class="has-error"><%=error%></li>
      <% end -%>
    </ul>
  <% end -%>
</form>
在我看来,在循环过程中保存新房间之前,我实际上是在调用authorize@room。我在我的
房间策略中创建了一个方法。rb

  def multiple_create?
    (user && record.hotel.account.admin == user) || (user && user.manager && (user.account == record.hotel.account))
  end
在我的API base_控制器中,我有:

after_action :verify_authorized, except: :index

为什么我会在这里遇到这个专家错误?

这是一个非常有创意的解决方案-但是有一种更简单、更好的方法来处理插入/更新多个记录

class Hotel
  has_many :rooms
  accepts_nested_attributes_for :rooms
end
意味着您可以使用以下内容创建房间:

@hotel = Hotel.new(
  rooms_attributes: [
     { foo: 'bar' },
     { foo: 'baz' }
  ]
)

@hotel.save # will insert both the hotel and rooms
您将创建一个如下所示的表单:

<%= form_for(@hotel) |f| %>
  <%= f.fields_for :rooms do |rf| %>
   <%= f.foo %>
  <% end %>
<% end %>
您可以使用多个
接受_嵌套的_属性_和
字段将其嵌套得更深。但一般来说,当你往下走一层以上的时候,这是一种严重的代码气味。将其拆分为多个控制器

请注意,您实际上不需要执行
create\u multiple
update\u multiple
操作

回到问题的核心,我如何验证它?保持简单愚蠢

并在您的
酒店政策中处理

def update?
  return false unless user
  record.account.admin == user || (user.manager && (user.account == record.account))
end
编辑 根据您对想要做什么的描述,您可以简单地在您的酒店模型上添加一个定制的getter/setter

class Hotel < ActiveRecord::Base
  has_many :rooms

  # custom getter for binding form inputs
  def number_of_rooms
    rooms.count
  end

  # custom setter which actually creates the associated records
  # this subtracts the existing number of rooms so that setting
  # hotel.number_of_rooms = 50 on an existing record with 20 rooms
  # will result in a total of 50 not 70.
  def number_of_rooms=(number)
    (number.to_i - number_of_rooms).times { rooms.new }
  end
end
此输入将位于
参数[:hotel][:房间数]
中。您应该将其添加到强参数白名单中


但是请注意,您将无法设置每个房间的属性。按照上面的建议进行授权。

谢谢@max。我不明白你为什么说我不需要多重创建操作。在我的表单中,我要做的是询问用户想要创建的每个房间的房间数和床位数(即
params[:room\u number]
params[:bed\u number]
bed\u number
room\u number
不是bed属性,而是room属性,它们只是我用来知道需要创建多少实例的一个因素。考虑到这一点,你是否仍然认为我不需要执行
多个创建
操作?不需要,你只需要对父记录执行修补程序请求。但是你应该这样做在循环中没有授权-只需检查一次用户是否可以更新/创建记录。很抱歉,我不明白如何在更新方法中实现多重创建操作?我应该如何使用我的参数[:room_number]和参数[:bed_number]?
<%= form_for(@hotel) |f| %>
  <%= f.fields_for :rooms do |rf| %>
   <%= f.foo %>
  <% end %>
<% end %>
class HotelsController

  def update
    @hotel.update(hotel_params)
    respond_with(@hotel)
  end

  private 
    def hotel_params
      params.require(:hotel).permit(rooms_attributes: [ :foo ])
    end
end
def update
  authorize @hotel
  # ...
end
def update?
  return false unless user
  record.account.admin == user || (user.manager && (user.account == record.account))
end
class Hotel < ActiveRecord::Base
  has_many :rooms

  # custom getter for binding form inputs
  def number_of_rooms
    rooms.count
  end

  # custom setter which actually creates the associated records
  # this subtracts the existing number of rooms so that setting
  # hotel.number_of_rooms = 50 on an existing record with 20 rooms
  # will result in a total of 50 not 70.
  def number_of_rooms=(number)
    (number.to_i - number_of_rooms).times { rooms.new }
  end
end
[14] pry(main)> h = Hotel.new(number_of_rooms: 3)
=> #<Hotel:0x007feeb6ee2b20 id: nil, created_at: nil, updated_at: nil>
[15] pry(main)> h.save
   (0.1ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "hotels" ("created_at", "updated_at") VALUES (?, ?)  [["created_at", "2016-02-08 13:17:06.723803"], ["updated_at", "2016-02-08 13:17:06.723803"]]
  SQL (0.2ms)  INSERT INTO "rooms" ("hotel_id", "created_at", "updated_at") VALUES (?, ?, ?)  [["hotel_id", 3], ["created_at", "2016-02-08 13:17:06.726157"], ["updated_at", "2016-02-08 13:17:06.726157"]]
  SQL (0.4ms)  INSERT INTO "rooms" ("hotel_id", "created_at", "updated_at") VALUES (?, ?, ?)  [["hotel_id", 3], ["created_at", "2016-02-08 13:17:06.728636"], ["updated_at", "2016-02-08 13:17:06.728636"]]
  SQL (0.1ms)  INSERT INTO "rooms" ("hotel_id", "created_at", "updated_at") VALUES (?, ?, ?)  [["hotel_id", 3], ["created_at", "2016-02-08 13:17:06.730291"], ["updated_at", "2016-02-08 13:17:06.730291"]]
   (1.6ms)  commit transaction
=> true
[16] pry(main)> 
<%= form_for(@hotel) |f| %>
  <% # ... %>
  <%= f.label :number_of_rooms %>
  <%= f.number_field :number_of_rooms %>
<% end %>