Ruby on rails Pundit在调用authorize对象的循环中引发AuthorizationNotPerformedError
我有两个rails应用程序: 一个是“前端应用程序”,负责显示数据、从用户获取输入并将数据发送到API(第二个应用程序)。第二个是处理数据库操作和向前端应用程序发送JSON的API 我有一个操作,在该操作中,我的用户决定他想在他的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[:
酒店中创建多少房间
,以及他应该在每个房间
中创建多少张床。前端应用程序上的表单如下所示:
<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 %>