英文:
Rails not permitting payload from JSON schema
问题
我正在试图弄清楚为什么我的Rails应用程序不允许我的属性,并且已经没有任何想法了。
我在我的数据库中有产品(Product模型),它们有一组适用于所有产品的标准属性。我有一个名为dynamic_attributes的JSONB列,它专门针对不同类型的产品。因此,我目前正在使用STI。
如果我尝试编辑单个记录,我会收到有关未经许可的参数(我试图从编辑表单中更新/补丁的JSON参数)的错误。如果我删除.permit部分,只是允许一切,那么我就不会收到错误,并且一切都会更新,但显然这不是理想的解决方案。
STI的Lock子类
class Lock < Product
serialize :dynamic_attributes, JsonbSerializers
store_accessor :dynamic_attributes, :kind, :interface
validates :kind, presence: true
def json_schema
Rails.root.join('app', 'models', 'schemas', 'lock_category.json')
end
end
它引用了产品类型的以下JSON模式:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"required": [ "kind" ],
"properties": {
"kind": {
"type": "string",
"enum": ["Cable", "Chain", "Folding", "Frame", "Part", "Padlock", "U-Lock", "U-Lock+Cable"]
},
"interface": {
"type": "string",
"enum": ["Key", "Combination", "Smart"]
}
}
}
产品控制器
def update
@product = Product.find(params[:id])
@product.update!(product_params)
redirect_to @product
end
private
def product_params
params.require(:product).
permit(
:type,
:vendor,
:description,
:cost,
:msrp,
dynamic_attributes: [
:kind,
:interface
]
)
end
end
编辑表单非常标准,用于更新dynamic_attributes的部分如下:
<% @product[:dynamic_attributes].each do |k,v| %>
<%= f.label k.to_sym %>
<%= f.text_field k.to_sym, :value => v %>
<br />
<% end %>
如果我要猜测(在这一点上我只能猜测),我认为问题可能在于从数据库加载信息到编辑字段,然后再次返回之间。
尝试PATCH的日志:
Started PATCH "/products/0c092119-601c-4dbb-b05b-3ad5ac638631" for 127.0.0.1 at 2023-03-09 12:41:06 -0600
Processing by ProductsController#update as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "product"=>{"vendor"=>"Vendor", "description"=>"First Lock", "cost"=>"37.5", "msrp"=>"74.99", "type"=>"Lock", "kind"=>"U-Lock", "interface"=>"Smart"}, "commit"=>"Update Product", "id"=>"0c092119-601c-4dbb-b05b-3ad5ac638631"}
Product Load (0.3ms) SELECT "products".* FROM "products" WHERE "products"."id" = $1 LIMIT $2 [["id", "0c092119-601c-4dbb-b05b-3ad5ac638631"], ["LIMIT", 1]]
↳ app/controllers/products_controller.rb:23:in `update'
未经许可的参数: :kind, :interface。上下文: { controller: ProductsController, action: update, request: #<ActionDispatch::Request:0x00007fbba812f500>, params: {"_method"=>"patch", "authenticity_token"=>"[FILTERED]", "product"=>{"vendor"=>"Vendor", "description"=>"First Lock", "cost"=>"37.5", "msrp"=>"74.99", "type"=>"Lock", "kind"=>"U-Lock", "interface"=>"Smart"}, "commit"=>"Update Product", "controller"=>"products", "action"=>"update", "id"=>"0c092119-601c-4dbb-b05b-3ad5ac638631"} }
TRANSACTION (0.1ms) BEGIN
↳ app/controllers/products_controller.rb:24:in `update'
Product Exists? (0.3ms) SELECT 1 AS one FROM "products" WHERE "products"."vpn" = $1 AND "products"."id" != $2 AND "products"."vendor" = $3 LIMIT $4 [["vpn", "453"], ["id", "0c092119-601c-4dbb-b05b-3ad5ac638631"], ["vendor", "Vendor"], ["LIMIT", 1]]
↳ app/controllers/products_controller.rb:24:in `update'
TRANSACTION (0.1ms) COMMIT
↳ app/controllers/products_controller.rb:24:in `update'
Redirected to http://localhost:3000/products/0c092119-601c-4dbb-b05b-3ad5ac638631
Completed 302 Found in 7ms (ActiveRecord: 0.7ms | Allocations: 5365)
英文:
I am trying to figure out why my rails app is not permitting my attributes and have run out of ideas.
I have products in my database (model Product) and they have a collection of standard attributes that apply to all products. I have an additional JSONB column called dynamic_attributes that is specific to different types of product. Due to this, I am currently using STI.
If I attempt to edit an individual record, I get an error regarding unpermitted parameters (the JSON parameters I am trying to update/patch from the edit form). If I remove the .permit section and just permit! everything then I get no errors and everything updates but obviously this isn't ideal.
Lock subclass for STI
class Lock < Product
serialize :dynamic_attributes, JsonbSerializers
store_accessor :dynamic_attributes, :kind, :interface
validates :kind, presence: true
def json_schema
Rails.root.join('app', 'models', 'schemas', 'lock_category.json')
end
end
which references the following JSON schema for the product type in question:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"required": [ "kind" ],
"properties": {
"kind": {
"type": "string",
"enum": ["Cable", "Chain", "Folding", "Frame", "Part", "Padlock", "U-Lock", "U-Lock+Cable"]
},
"interface": {
"type": "string",
"enum": ["Key", "Combination", "Smart"]
}
}
}
Products Controller
def update
@product = Product.find(params[:id])
@product.update!(product_params)
redirect_to @product
end
private
def product_params
params.require(:product).
permit(
:type,
:vendor,
:description,
:cost,
:msrp,
dynamic_attributes: [
:kind,
:interface
]
)
end
end
and the edit form is pretty standard with the following section for updating the dynamic_attributes:
<% @product[:dynamic_attributes].each do |k,v| %>
<%= f.label k.to_sym %>
<%= f.text_field k.to_sym, :value => v %>
<br />
<% end %>
If I were to guess (all I have left at this point, I think it is probably something between loading the information from the database into the edit field and then back again.
The log from the attempted PATCH:
Started PATCH "/products/0c092119-601c-4dbb-b05b-3ad5ac638631" for 127.0.0.1 at 2023-03-09 12:41:06 -0600
Processing by ProductsController#update as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "product"=>{"vendor"=>"Vendor", "description"=>"First Lock", "cost"=>"37.5", "msrp"=>"74.99", "type"=>"Lock", "kind"=>"U-Lock", "interface"=>"Smart"}, "commit"=>"Update Product", "id"=>"0c092119-601c-4dbb-b05b-3ad5ac638631"}
Product Load (0.3ms) SELECT "products".* FROM "products" WHERE "products"."id" = $1 LIMIT $2 [["id", "0c092119-601c-4dbb-b05b-3ad5ac638631"], ["LIMIT", 1]]
↳ app/controllers/products_controller.rb:23:in `update'
Unpermitted parameters: :kind, :interface. Context: { controller: ProductsController, action: update, request: #<ActionDispatch::Request:0x00007fbba812f500>, params: {"_method"=>"patch", "authenticity_token"=>"[FILTERED]", "product"=>{"vendor"=>"Vendor", "description"=>"First Lock", "cost"=>"37.5", "msrp"=>"74.99", "type"=>"Lock", "kind"=>"U-Lock", "interface"=>"Smart"}, "commit"=>"Update Product", "controller"=>"products", "action"=>"update", "id"=>"0c092119-601c-4dbb-b05b-3ad5ac638631"} }
TRANSACTION (0.1ms) BEGIN
↳ app/controllers/products_controller.rb:24:in `update'
Product Exists? (0.3ms) SELECT 1 AS one FROM "products" WHERE "products"."vpn" = $1 AND "products"."id" != $2 AND "products"."vendor" = $3 LIMIT $4 [["vpn", "453"], ["id", "0c092119-601c-4dbb-b05b-3ad5ac638631"], ["vendor", "Vendor"], ["LIMIT", 1]]
↳ app/controllers/products_controller.rb:24:in `update'
TRANSACTION (0.1ms) COMMIT
↳ app/controllers/products_controller.rb:24:in `update'
Redirected to http://localhost:3000/products/0c092119-601c-4dbb-b05b-3ad5ac638631
Completed 302 Found in 7ms (ActiveRecord: 0.7ms | Allocations: 5365)
答案1
得分: 2
感谢 @engineersmnky 给出的答案:
"kind" 和 "interface" 不被允许,因为您声明它们应该嵌套在 "dynamic_attributes" 下,但在请求中它们没有嵌套。也许尝试更改如下代码:
f.text_field k.to_sym, to: f.text_field("dynamic_attributes[#{k}]")
所以我根据这个更新了我的代码:
<% @product[:dynamic_attributes].each do |k,v| %>
<%= f.label k.to_sym %>
<%= f.text_field "dynamic_attributes[#{k}]", :value => v %>
<% end %>
英文:
Thanks to @engineersmnky who had the answer:
kind, and interface are not permitted because you are stating that they should be nested under dynamic_attributes but in the request they are not nested. Maybe try changing this
f.text_field k.to_sym to f.text_field "dyanmic_attributes[#{k}]"
So I updated my code accordingly to:
<% @product[:dynamic_attributes].each do |k,v| %>
<%= f.label k.to_sym %>
<%= f.text_field "dynamic_attributes[#{k}]", :value => v %>
<% end %>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论